From b6644de6e1eb863d3e82d0e294797a81582e95af Mon Sep 17 00:00:00 2001
From: anlicheng <244108715@qq.com>
Date: Tue, 23 Sep 2025 15:37:44 +0800
Subject: [PATCH] fix
---
apps/efka/src/docker/docker_uds_commands.erl | 327 ++++++++++++-------
1 file changed, 204 insertions(+), 123 deletions(-)
diff --git a/apps/efka/src/docker/docker_uds_commands.erl b/apps/efka/src/docker/docker_uds_commands.erl
index c29d2cb..4631801 100644
--- a/apps/efka/src/docker/docker_uds_commands.erl
+++ b/apps/efka/src/docker/docker_uds_commands.erl
@@ -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,188 +147,267 @@ 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.
+%% 构建最终 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_networks(Config),
+ build_labels(Config),
+ build_restart(Config),
+ build_user(Config),
+ build_working_dir(Config),
+ build_hostname(Config),
+ build_privileged(Config),
+ build_cap_add_drop(Config),
+ build_devices(Config),
+ build_memory(Config),
+ build_cpu(Config),
+ build_ulimits(Config),
+ build_sysctls(Config),
+ build_tmpfs(Config),
+ build_extra_hosts(Config),
+ build_healthcheck(Config)
+ ])
+ ).
-%% 构建所有参数
-build_options(Config) ->
- lists:flatten([
- build_entrypoint(Config),
- build_ports(Config),
- build_expose(Config),
- build_volumes(Config),
- build_env(Config),
- build_env_file(Config),
- build_networks(Config),
- build_labels(Config),
- build_restart(Config),
- build_user(Config),
- build_working_dir(Config),
- build_hostname(Config),
- build_privileged(Config),
- build_cap_add_drop(Config),
- build_devices(Config),
- build_memory(Config),
- build_cpu(Config),
- build_ulimits(Config),
- build_sysctls(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 <- 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">>, <>] 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
- CPU /= undefined ->
- Bin = iolist_to_binary(io_lib:format("~p", [CPU])),
- [<<"--cpus">>, Bin];
- true ->
- []
- end,
- Res2 = if
- Shares /= undefined ->
- Bin1 = iolist_to_binary(io_lib:format("~p", [Shares])),
- [<<"--cpu-shares">>, Bin1];
- true ->
- []
- end,
- Res1 ++ Res2.
+ HCfg = #{},
+ HCfg1 = if
+ CPU /= undefined ->
+ maps:put(<<"NanoCpus">>, trunc(CPU * 1000000000), HCfg);
+ true ->
+ HCfg
+ end,
+ HCfg2 = if
+ Shares /= undefined ->
+ maps:put(<<"CpuShares">>, Shares, HCfg1);
+ true ->
+ HCfg1
+ end,
+ case maps:size(HCfg2) of
+ 0 -> #{};
+ _ -> #{<<"HostConfig">> => HCfg2}
+ end.
build_ulimits(Config) ->
- UL = maps:get(<<"ulimits">>, Config, #{}),
- lists:map(fun({K, V}) -> [<<"--ulimit">>, <>] end, maps:to_list(UL)).
+ UL = maps:get(<<"ulimits">>, Config, #{}),
+ 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 ">>, <>] 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">>];
- _ ->
- []
- end;
- <<"interval">> ->
- [<<"--health-interval">>, V];
- <<"timeout">> ->
- [<<"--health-timeout">>, V];
- <<"retries">> ->
- [<<"--health-retries">>, io_lib:format("~p", [V])];
- _ ->
- []
- end
- end, maps:to_list(HC)).
+ case maps:size(HC) of
+ 0 -> #{};
+ _ ->
+ 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;
+ _ ->
+ N
+ end.
\ No newline at end of file