This commit is contained in:
anlicheng 2025-09-23 15:37:44 +08:00
parent f00f654e7a
commit b6644de6e1

View File

@ -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()}. -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) -> create_container(ContainerName, ContainerDir, Config) when is_binary(ContainerName), is_list(ContainerDir), is_map(Config) ->
Url = "/containers/create",
Image = maps:get(<<"image">>, Config), Image = maps:get(<<"image">>, Config),
Cmd = maps:get(<<"command">>, Config, []), Cmd = maps:get(<<"command">>, Config, []),
@ -145,25 +147,18 @@ gather_output(Port, Acc) ->
{Status, iolist_to_binary(Acc)} {Status, iolist_to_binary(Acc)}
end. end.
-spec gather_pull_output(Port :: port(), Callback :: fun((Msg :: binary()) -> no_return())) -> ExitCode :: integer(). %% JSON Map
gather_pull_output(Port, Callback) -> build_options(Config) when is_map(Config) ->
receive maps:merge(
{Port, {data, Data}} -> #{
Callback(Data), <<"Image">> => maps:get(<<"image">>, Config, <<>>),
gather_pull_output(Port, Callback); <<"Cmd">> => maps:get(<<"command">>, Config, []),
{Port, {exit_status, Status}} -> <<"Entrypoint">> => maps:get(<<"entrypoint">>, Config, []),
Status <<"Env">> => maps:get(<<"envs">>, Config, [])
end. },
fold_merge([
%%
build_options(Config) ->
lists:flatten([
build_entrypoint(Config),
build_ports(Config),
build_expose(Config), build_expose(Config),
build_volumes(Config), build_volumes(Config),
build_env(Config),
build_env_file(Config),
build_networks(Config), build_networks(Config),
build_labels(Config), build_labels(Config),
build_restart(Config), build_restart(Config),
@ -180,153 +175,239 @@ build_options(Config) ->
build_tmpfs(Config), build_tmpfs(Config),
build_extra_hosts(Config), build_extra_hosts(Config),
build_healthcheck(Config) build_healthcheck(Config)
]). ])
).
build_entrypoint(Config) -> %%
case maps:get(<<"entrypoint">>, Config, []) of fold_merge(List) ->
[] -> []; lists:foldl(fun maps:merge/2, #{}, List).
EP -> [<<"--entrypoint">> | EP]
end.
build_ports(Config) ->
Ports = maps:get(<<"ports">>, Config, []),
lists:map(fun(P) -> [<<"-p">>, P] end, Ports).
%% --- ---
build_expose(Config) -> build_expose(Config) ->
Ports = maps:get(<<"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) -> build_volumes(Config) ->
Vols = maps:get(<<"volumes">>, Config, []), Vols = maps:get(<<"volumes">>, Config, []),
lists:map(fun(V) -> [<<"-v">>, V] end, Vols). case Vols of
[] -> #{};
build_env(Config) -> _ ->
Envs = maps:get(<<"envs">>, Config, []), HostBinds = Vols,
lists:map(fun(E) -> [<<"-e">>, E] end, Envs). VolMap = maps:from_list(lists:map(fun(V) ->
[_Host, Cont] = binary:split(V, <<":">>, []),
build_env_file(Config) -> {Cont, #{}}
Files = maps:get(<<"env_file">>, Config, []), end, Vols)),
lists:map(fun(F) -> [<<"--env-file">>, F] end, Files). #{
<<"HostConfig">> => #{
<<"Binds">> => HostBinds
},
<<"Volumes">> => VolMap
}
end.
build_networks(Config) -> build_networks(Config) ->
Nets = maps:get(<<"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) -> build_labels(Config) ->
case maps:get(<<"labels">>, Config, #{}) of Labels = maps:get(<<"labels">>, Config, #{}),
#{} -> case maps:size(Labels) of
[]; 0 -> #{};
Labels -> _ -> #{<<"Labels">> => Labels}
lists:map(fun({K, V}) -> [<<"--label">>, <<K/binary, "=", V/binary>>] end, maps:to_list(Labels))
end. end.
build_restart(Config) -> build_restart(Config) ->
case maps:get(<<"restart">>, Config, undefined) of case maps:get(<<"restart">>, Config, undefined) of
undefined -> []; undefined ->
Policy -> [<<"--restart">>, Policy] #{};
Policy ->
#{<<"HostConfig">> => #{<<"RestartPolicy">> => #{<<"Name">> => Policy}}}
end. end.
build_user(Config) -> build_user(Config) ->
case maps:get(<<"user">>, Config, undefined) of case maps:get(<<"user">>, Config, undefined) of
undefined -> []; undefined ->
U -> [<<"--user">>, U] #{};
U ->
#{<<"User">> => U}
end. end.
build_working_dir(Config) -> build_working_dir(Config) ->
case maps:get(<<"working_dir">>, Config, undefined) of case maps:get(<<"working_dir">>, Config, undefined) of
undefined -> []; undefined ->
D -> [<<"--workdir">>, D] #{};
D ->
#{<<"WorkingDir">> => D}
end. end.
build_hostname(Config) -> build_hostname(Config) ->
case maps:get(<<"hostname">>, Config, undefined) of case maps:get(<<"hostname">>, Config, undefined) of
undefined -> []; undefined ->
H -> [<<"--hostname">>, H] #{};
H ->
#{<<"Hostname">> => H}
end. end.
build_privileged(Config) -> build_privileged(Config) ->
case maps:get(<<"privileged">>, Config, false) of case maps:get(<<"privileged">>, Config, false) of
true -> [<<"--privileged">>]; true ->
_ -> [] #{<<"HostConfig">> => #{<<"Privileged">> => true}};
_ ->
#{}
end. end.
build_cap_add_drop(Config) -> build_cap_add_drop(Config) ->
Add = maps:get(<<"cap_add">>, Config, []), Add = maps:get(<<"cap_add">>, Config, []),
Drop = maps:get(<<"cap_drop">>, 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) -> build_devices(Config) ->
Devs = maps:get(<<"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) -> build_memory(Config) ->
Mem = maps:get(<<"mem_limit">>, Config, undefined), Mem = maps:get(<<"mem_limit">>, Config, undefined),
MemRes = maps:get(<<"mem_reservation">>, Config, undefined), MemRes = maps:get(<<"mem_reservation">>, Config, undefined),
Res1 = if Mem /= undefined -> [<<"--memory">>, Mem]; true -> [] end, HCfg = #{},
Res2 = if MemRes /= undefined -> [<<"--memory-reservation">>, MemRes]; true -> [] end, HCfg1 = if
Res1 ++ Res2. 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) -> build_cpu(Config) ->
CPU = maps:get(<<"cpus">>, Config, undefined), CPU = maps:get(<<"cpus">>, Config, undefined),
Shares = maps:get(<<"cpu_shares">>, Config, undefined), Shares = maps:get(<<"cpu_shares">>, Config, undefined),
Res1 = if HCfg = #{},
HCfg1 = if
CPU /= undefined -> CPU /= undefined ->
Bin = iolist_to_binary(io_lib:format("~p", [CPU])), maps:put(<<"NanoCpus">>, trunc(CPU * 1000000000), HCfg);
[<<"--cpus">>, Bin];
true -> true ->
[] HCfg
end, end,
Res2 = if HCfg2 = if
Shares /= undefined -> Shares /= undefined ->
Bin1 = iolist_to_binary(io_lib:format("~p", [Shares])), maps:put(<<"CpuShares">>, Shares, HCfg1);
[<<"--cpu-shares">>, Bin1];
true -> true ->
[] HCfg1
end, end,
Res1 ++ Res2. case maps:size(HCfg2) of
0 -> #{};
_ -> #{<<"HostConfig">> => HCfg2}
end.
build_ulimits(Config) -> build_ulimits(Config) ->
UL = maps:get(<<"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) -> build_sysctls(Config) ->
SC = maps:get(<<"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) -> build_tmpfs(Config) ->
Tmp = maps:get(<<"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) -> build_extra_hosts(Config) ->
Hosts = maps:get(<<"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) -> build_healthcheck(Config) ->
HC = maps:get(<<"healthcheck">>, Config, #{}), HC = maps:get(<<"healthcheck">>, Config, #{}),
lists:map(fun({K, V}) -> case maps:size(HC) of
case K of 0 -> #{};
<<"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">>];
_ -> _ ->
[] HCMap = #{
end; <<"Test">> => maps:get(<<"test">>, HC, []),
<<"interval">> -> <<"Interval">> => parse_duration(maps:get(<<"interval">>, HC, <<"0s">>)),
[<<"--health-interval">>, V]; <<"Timeout">> => parse_duration(maps:get(<<"timeout">>, HC, <<"0s">>)),
<<"timeout">> -> <<"Retries">> => maps:get(<<"retries">>, HC, 0)
[<<"--health-timeout">>, V]; },
<<"retries">> -> #{<<"Healthcheck">> => HCMap}
[<<"--health-retries">>, io_lib:format("~p", [V])]; 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;
_ -> _ ->
[] N
end end.
end, maps:to_list(HC)).