fix
This commit is contained in:
parent
f00f654e7a
commit
b6644de6e1
@ -36,6 +36,8 @@ check_image_exist(Image) when is_binary(Image) ->
|
||||
|
||||
-spec create_container(ContainerName :: binary(), ContainerDir :: string(), Config :: map()) -> {ok, ContainerId :: binary()} | {error, Reason :: any()}.
|
||||
create_container(ContainerName, ContainerDir, Config) when is_binary(ContainerName), is_list(ContainerDir), is_map(Config) ->
|
||||
Url = "/containers/create",
|
||||
|
||||
Image = maps:get(<<"image">>, Config),
|
||||
Cmd = maps:get(<<"command">>, Config, []),
|
||||
|
||||
@ -145,25 +147,18 @@ gather_output(Port, Acc) ->
|
||||
{Status, iolist_to_binary(Acc)}
|
||||
end.
|
||||
|
||||
-spec gather_pull_output(Port :: port(), Callback :: fun((Msg :: binary()) -> no_return())) -> ExitCode :: integer().
|
||||
gather_pull_output(Port, Callback) ->
|
||||
receive
|
||||
{Port, {data, Data}} ->
|
||||
Callback(Data),
|
||||
gather_pull_output(Port, Callback);
|
||||
{Port, {exit_status, Status}} ->
|
||||
Status
|
||||
end.
|
||||
|
||||
%% 构建所有参数
|
||||
build_options(Config) ->
|
||||
lists:flatten([
|
||||
build_entrypoint(Config),
|
||||
build_ports(Config),
|
||||
%% 构建最终 JSON Map
|
||||
build_options(Config) when is_map(Config) ->
|
||||
maps:merge(
|
||||
#{
|
||||
<<"Image">> => maps:get(<<"image">>, Config, <<>>),
|
||||
<<"Cmd">> => maps:get(<<"command">>, Config, []),
|
||||
<<"Entrypoint">> => maps:get(<<"entrypoint">>, Config, []),
|
||||
<<"Env">> => maps:get(<<"envs">>, Config, [])
|
||||
},
|
||||
fold_merge([
|
||||
build_expose(Config),
|
||||
build_volumes(Config),
|
||||
build_env(Config),
|
||||
build_env_file(Config),
|
||||
build_networks(Config),
|
||||
build_labels(Config),
|
||||
build_restart(Config),
|
||||
@ -180,153 +175,239 @@ build_options(Config) ->
|
||||
build_tmpfs(Config),
|
||||
build_extra_hosts(Config),
|
||||
build_healthcheck(Config)
|
||||
]).
|
||||
])
|
||||
).
|
||||
|
||||
build_entrypoint(Config) ->
|
||||
case maps:get(<<"entrypoint">>, Config, []) of
|
||||
[] -> [];
|
||||
EP -> [<<"--entrypoint">> | EP]
|
||||
end.
|
||||
|
||||
build_ports(Config) ->
|
||||
Ports = maps:get(<<"ports">>, Config, []),
|
||||
lists:map(fun(P) -> [<<"-p">>, P] end, Ports).
|
||||
%% 工具函数
|
||||
fold_merge(List) ->
|
||||
lists:foldl(fun maps:merge/2, #{}, List).
|
||||
|
||||
%% --- 构建子字段 ---
|
||||
build_expose(Config) ->
|
||||
Ports = maps:get(<<"expose">>, Config, []),
|
||||
lists:map(fun(P) -> [<<"--expose">>, P] end, Ports).
|
||||
case Ports of
|
||||
[] -> #{};
|
||||
_ ->
|
||||
Exposed = maps:from_list([{<<P/binary, "/tcp">>, #{}} || P <- Ports]),
|
||||
#{<<"ExposedPorts">> => Exposed}
|
||||
end.
|
||||
|
||||
build_volumes(Config) ->
|
||||
Vols = maps:get(<<"volumes">>, Config, []),
|
||||
lists:map(fun(V) -> [<<"-v">>, V] end, Vols).
|
||||
|
||||
build_env(Config) ->
|
||||
Envs = maps:get(<<"envs">>, Config, []),
|
||||
lists:map(fun(E) -> [<<"-e">>, E] end, Envs).
|
||||
|
||||
build_env_file(Config) ->
|
||||
Files = maps:get(<<"env_file">>, Config, []),
|
||||
lists:map(fun(F) -> [<<"--env-file">>, F] end, Files).
|
||||
case Vols of
|
||||
[] -> #{};
|
||||
_ ->
|
||||
HostBinds = Vols,
|
||||
VolMap = maps:from_list(lists:map(fun(V) ->
|
||||
[_Host, Cont] = binary:split(V, <<":">>, []),
|
||||
{Cont, #{}}
|
||||
end, Vols)),
|
||||
#{
|
||||
<<"HostConfig">> => #{
|
||||
<<"Binds">> => HostBinds
|
||||
},
|
||||
<<"Volumes">> => VolMap
|
||||
}
|
||||
end.
|
||||
|
||||
build_networks(Config) ->
|
||||
Nets = maps:get(<<"networks">>, Config, []),
|
||||
lists:map(fun(Net) -> [<<"--network">>, Net] end, Nets).
|
||||
case Nets of
|
||||
[] -> #{};
|
||||
_ ->
|
||||
NetCfg = maps:from_list([{N, #{}} || N <- Nets]),
|
||||
#{<<"NetworkingConfig">> => #{<<"EndpointsConfig">> => NetCfg}}
|
||||
end.
|
||||
|
||||
build_labels(Config) ->
|
||||
case maps:get(<<"labels">>, Config, #{}) of
|
||||
#{} ->
|
||||
[];
|
||||
Labels ->
|
||||
lists:map(fun({K, V}) -> [<<"--label">>, <<K/binary, "=", V/binary>>] end, maps:to_list(Labels))
|
||||
Labels = maps:get(<<"labels">>, Config, #{}),
|
||||
case maps:size(Labels) of
|
||||
0 -> #{};
|
||||
_ -> #{<<"Labels">> => Labels}
|
||||
end.
|
||||
|
||||
build_restart(Config) ->
|
||||
case maps:get(<<"restart">>, Config, undefined) of
|
||||
undefined -> [];
|
||||
Policy -> [<<"--restart">>, Policy]
|
||||
undefined ->
|
||||
#{};
|
||||
Policy ->
|
||||
#{<<"HostConfig">> => #{<<"RestartPolicy">> => #{<<"Name">> => Policy}}}
|
||||
end.
|
||||
|
||||
build_user(Config) ->
|
||||
case maps:get(<<"user">>, Config, undefined) of
|
||||
undefined -> [];
|
||||
U -> [<<"--user">>, U]
|
||||
undefined ->
|
||||
#{};
|
||||
U ->
|
||||
#{<<"User">> => U}
|
||||
end.
|
||||
|
||||
build_working_dir(Config) ->
|
||||
case maps:get(<<"working_dir">>, Config, undefined) of
|
||||
undefined -> [];
|
||||
D -> [<<"--workdir">>, D]
|
||||
undefined ->
|
||||
#{};
|
||||
D ->
|
||||
#{<<"WorkingDir">> => D}
|
||||
end.
|
||||
|
||||
build_hostname(Config) ->
|
||||
case maps:get(<<"hostname">>, Config, undefined) of
|
||||
undefined -> [];
|
||||
H -> [<<"--hostname">>, H]
|
||||
undefined ->
|
||||
#{};
|
||||
H ->
|
||||
#{<<"Hostname">> => H}
|
||||
end.
|
||||
|
||||
build_privileged(Config) ->
|
||||
case maps:get(<<"privileged">>, Config, false) of
|
||||
true -> [<<"--privileged">>];
|
||||
_ -> []
|
||||
true ->
|
||||
#{<<"HostConfig">> => #{<<"Privileged">> => true}};
|
||||
_ ->
|
||||
#{}
|
||||
end.
|
||||
|
||||
build_cap_add_drop(Config) ->
|
||||
Add = maps:get(<<"cap_add">>, Config, []),
|
||||
Drop = maps:get(<<"cap_drop">>, Config, []),
|
||||
lists:map(fun(C) -> [<<"--cap-add">>, C] end, Add) ++ lists:map(fun(C0) -> [<<"--cap-drop">>, C0] end, Drop).
|
||||
case {Add, Drop} of
|
||||
{[], []} ->
|
||||
#{};
|
||||
_ ->
|
||||
#{<<"HostConfig">> => #{<<"CapAdd">> => Add, <<"CapDrop">> => Drop}}
|
||||
end.
|
||||
|
||||
build_devices(Config) ->
|
||||
Devs = maps:get(<<"devices">>, Config, []),
|
||||
lists:map(fun(D) -> [<<"--device">>, D] end, Devs).
|
||||
case Devs of
|
||||
[] ->
|
||||
#{};
|
||||
_ ->
|
||||
DevObjs = [#{<<"PathOnHost">> => H, <<"PathInContainer">> => C,
|
||||
<<"CgroupPermissions">> => <<"rwm">>}
|
||||
|| D <- Devs,
|
||||
[H, C] <- [binary:split(D, <<":">>, [])]],
|
||||
#{<<"HostConfig">> => #{<<"Devices">> => DevObjs}}
|
||||
end.
|
||||
|
||||
build_memory(Config) ->
|
||||
Mem = maps:get(<<"mem_limit">>, Config, undefined),
|
||||
MemRes = maps:get(<<"mem_reservation">>, Config, undefined),
|
||||
Res1 = if Mem /= undefined -> [<<"--memory">>, Mem]; true -> [] end,
|
||||
Res2 = if MemRes /= undefined -> [<<"--memory-reservation">>, MemRes]; true -> [] end,
|
||||
Res1 ++ Res2.
|
||||
HCfg = #{},
|
||||
HCfg1 = if
|
||||
Mem /= undefined ->
|
||||
maps:put(<<"Memory">>, parse_mem(Mem), HCfg);
|
||||
true ->
|
||||
HCfg
|
||||
end,
|
||||
HCfg2 = if
|
||||
MemRes /= undefined ->
|
||||
maps:put(<<"MemoryReservation">>, parse_mem(MemRes), HCfg1);
|
||||
true ->
|
||||
HCfg1
|
||||
end,
|
||||
case maps:size(HCfg2) of
|
||||
0 -> #{};
|
||||
_ -> #{<<"HostConfig">> => HCfg2}
|
||||
end.
|
||||
|
||||
parse_mem(Val) ->
|
||||
case binary:last(Val) of
|
||||
$m ->
|
||||
N = binary:part(Val, {0, byte_size(Val)-1}),
|
||||
list_to_integer(binary_to_list(N)) * 1024 * 1024;
|
||||
$g ->
|
||||
N = binary:part(Val, {0, byte_size(Val)-1}),
|
||||
list_to_integer(binary_to_list(N)) * 1024 * 1024 * 1024;
|
||||
_ ->
|
||||
list_to_integer(binary_to_list(Val))
|
||||
end.
|
||||
|
||||
build_cpu(Config) ->
|
||||
CPU = maps:get(<<"cpus">>, Config, undefined),
|
||||
Shares = maps:get(<<"cpu_shares">>, Config, undefined),
|
||||
Res1 = if
|
||||
HCfg = #{},
|
||||
HCfg1 = if
|
||||
CPU /= undefined ->
|
||||
Bin = iolist_to_binary(io_lib:format("~p", [CPU])),
|
||||
[<<"--cpus">>, Bin];
|
||||
maps:put(<<"NanoCpus">>, trunc(CPU * 1000000000), HCfg);
|
||||
true ->
|
||||
[]
|
||||
HCfg
|
||||
end,
|
||||
Res2 = if
|
||||
HCfg2 = if
|
||||
Shares /= undefined ->
|
||||
Bin1 = iolist_to_binary(io_lib:format("~p", [Shares])),
|
||||
[<<"--cpu-shares">>, Bin1];
|
||||
maps:put(<<"CpuShares">>, Shares, HCfg1);
|
||||
true ->
|
||||
[]
|
||||
HCfg1
|
||||
end,
|
||||
Res1 ++ Res2.
|
||||
case maps:size(HCfg2) of
|
||||
0 -> #{};
|
||||
_ -> #{<<"HostConfig">> => HCfg2}
|
||||
end.
|
||||
|
||||
build_ulimits(Config) ->
|
||||
UL = maps:get(<<"ulimits">>, Config, #{}),
|
||||
lists:map(fun({K, V}) -> [<<"--ulimit">>, <<K/binary, "=", V/binary>>] end, maps:to_list(UL)).
|
||||
case maps:size(UL) of
|
||||
0 -> #{};
|
||||
_ ->
|
||||
ULList = [#{<<"Name">> => K, <<"Soft">> => S, <<"Hard">> => H}
|
||||
|| {K, V} <- maps:to_list(UL),
|
||||
[S1, H1] <- [binary:split(V, <<":">>, [])],
|
||||
S = list_to_integer(binary_to_list(S1)),
|
||||
H = list_to_integer(binary_to_list(H1))],
|
||||
|
||||
#{<<"HostConfig">> => #{<<"Ulimits">> => ULList}}
|
||||
end.
|
||||
|
||||
build_sysctls(Config) ->
|
||||
SC = maps:get(<<"sysctls">>, Config, #{}),
|
||||
lists:map(fun({K, V}) -> [<<"--sysctl ">>, <<K/binary, "=", V/binary>>] end, maps:to_list(SC)).
|
||||
case maps:size(SC) of
|
||||
0 ->
|
||||
#{};
|
||||
_ ->
|
||||
#{<<"HostConfig">> => #{<<"Sysctls">> => SC}}
|
||||
end.
|
||||
|
||||
build_tmpfs(Config) ->
|
||||
Tmp = maps:get(<<"tmpfs">>, Config, []),
|
||||
lists:map(fun(T) -> [<<"--tmpfs">>, T] end, Tmp).
|
||||
case Tmp of
|
||||
[] ->
|
||||
#{};
|
||||
_ ->
|
||||
#{<<"HostConfig">> => #{<<"Tmpfs">> => maps:from_list([{T, <<>>} || T <- Tmp])}}
|
||||
end.
|
||||
|
||||
build_extra_hosts(Config) ->
|
||||
Hosts = maps:get(<<"extra_hosts">>, Config, []),
|
||||
lists:map(fun(H) -> [<<"--add-host">>, H] end, Hosts).
|
||||
case Hosts of
|
||||
[] ->
|
||||
#{};
|
||||
_ ->
|
||||
#{<<"HostConfig">> => #{<<"ExtraHosts">> => Hosts}}
|
||||
end.
|
||||
|
||||
build_healthcheck(Config) ->
|
||||
HC = maps:get(<<"healthcheck">>, Config, #{}),
|
||||
lists:map(fun({K, V}) ->
|
||||
case K of
|
||||
<<"test">> ->
|
||||
case V of
|
||||
%% Test 是 ["CMD-SHELL", Cmd]
|
||||
[<<"CMD-SHELL">>, Cmd] ->
|
||||
[<<"--health-cmd">>, <<$", Cmd/binary, $">>];
|
||||
%% Test 是 ["CMD", Arg1, Arg2...]
|
||||
[<<"CMD">> | CmdList] ->
|
||||
CmdArgs = iolist_to_binary(lists:join(<<" ">>, CmdList)),
|
||||
[<<"--health-cmd">>, <<$", CmdArgs/binary, $">>];
|
||||
%% Test 是 <<"NONE">>
|
||||
[<<"NONE">>] ->
|
||||
[<<"--no-healthcheck">>];
|
||||
case maps:size(HC) of
|
||||
0 -> #{};
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
<<"interval">> ->
|
||||
[<<"--health-interval">>, V];
|
||||
<<"timeout">> ->
|
||||
[<<"--health-timeout">>, V];
|
||||
<<"retries">> ->
|
||||
[<<"--health-retries">>, io_lib:format("~p", [V])];
|
||||
HCMap = #{
|
||||
<<"Test">> => maps:get(<<"test">>, HC, []),
|
||||
<<"Interval">> => parse_duration(maps:get(<<"interval">>, HC, <<"0s">>)),
|
||||
<<"Timeout">> => parse_duration(maps:get(<<"timeout">>, HC, <<"0s">>)),
|
||||
<<"Retries">> => maps:get(<<"retries">>, HC, 0)
|
||||
},
|
||||
#{<<"Healthcheck">> => HCMap}
|
||||
end.
|
||||
|
||||
parse_duration(Bin) ->
|
||||
%% "30s" -> 30000000000
|
||||
Sz = byte_size(Bin),
|
||||
NBin = binary:part(Bin, {0, Sz-1}),
|
||||
N = list_to_integer(binary_to_list(NBin)),
|
||||
case binary:last(Bin) of
|
||||
$s ->
|
||||
N * 1000000000;
|
||||
$m ->
|
||||
N * 60000000000;
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end, maps:to_list(HC)).
|
||||
N
|
||||
end.
|
||||
Loading…
x
Reference in New Issue
Block a user