fix builder
This commit is contained in:
parent
72030d55d6
commit
ac4fabad1d
@ -40,10 +40,7 @@ create_container(ContainerDir, #'ContainerDeployParams'{
|
|||||||
}) when is_binary(ContainerName), is_list(ContainerDir) ->
|
}) when is_binary(ContainerName), is_list(ContainerDir) ->
|
||||||
|
|
||||||
Url = lists:flatten(io_lib:format("/containers/create?name=~s", [binary_to_list(ContainerName)])),
|
Url = lists:flatten(io_lib:format("/containers/create?name=~s", [binary_to_list(ContainerName)])),
|
||||||
%% 挂载预留的目录,用来作为配置文件的存放
|
Options = docker_container_builder:build_options(ContainerName, ContainerDir, Create0),
|
||||||
ConfigFile = list_to_binary(docker_helper:get_config_file(ContainerDir)),
|
|
||||||
Create = patch_create_options(ContainerName, ConfigFile, Create0),
|
|
||||||
Options = build_options(Create),
|
|
||||||
display_options(Options),
|
display_options(Options),
|
||||||
|
|
||||||
Body = iolist_to_binary(jiffy:encode(Options, [force_utf8])),
|
Body = iolist_to_binary(jiffy:encode(Options, [force_utf8])),
|
||||||
@ -230,311 +227,11 @@ inspect_container(ContainerId) when is_binary(ContainerId) ->
|
|||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%%% helper methods
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
|
|
||||||
patch_create_options(ContainerName, ConfigFile, undefined) ->
|
|
||||||
patch_create_options(ContainerName, ConfigFile, #'DockerCreateOptions'{});
|
|
||||||
patch_create_options(ContainerName, ConfigFile, Create = #'DockerCreateOptions'{
|
|
||||||
config = Config0,
|
|
||||||
host_config = HostConfig0
|
|
||||||
}) when is_binary(ContainerName), is_binary(ConfigFile) ->
|
|
||||||
Config = patch_container_config(ContainerName, ensure_container_config(Config0)),
|
|
||||||
HostConfig = patch_host_config(ConfigFile, ensure_host_config(HostConfig0)),
|
|
||||||
Create#'DockerCreateOptions'{config = Config, host_config = HostConfig}.
|
|
||||||
|
|
||||||
patch_container_config(ContainerName, Config = #'DockerContainerConfig'{env = Env0, volumes = Volumes0})
|
|
||||||
when is_binary(ContainerName) ->
|
|
||||||
ConfigVolume = <<"/usr/local/etc/service.conf">>,
|
|
||||||
Envs0 = [<<"CONTAINER_NAME=", ContainerName/binary>>],
|
|
||||||
Envs = add_unique_front(Envs0, [to_binary(EnvItem) || EnvItem <- Env0]),
|
|
||||||
Volumes = add_unique_front([ConfigVolume], [to_binary(Volume) || Volume <- Volumes0]),
|
|
||||||
Config#'DockerContainerConfig'{env = Envs, volumes = Volumes}.
|
|
||||||
|
|
||||||
patch_host_config(ConfigFile, HostConfig = #'DockerHostConfig'{binds = Binds0}) when is_binary(ConfigFile) ->
|
|
||||||
ConfigBind = <<ConfigFile/binary, ":/usr/local/etc/service.conf">>,
|
|
||||||
Binds = add_unique_front([ConfigBind], [to_binary(Bind) || Bind <- Binds0]),
|
|
||||||
HostConfig#'DockerHostConfig'{binds = Binds}.
|
|
||||||
|
|
||||||
ensure_container_config(undefined) ->
|
|
||||||
#'DockerContainerConfig'{};
|
|
||||||
ensure_container_config(Config = #'DockerContainerConfig'{}) ->
|
|
||||||
Config.
|
|
||||||
|
|
||||||
ensure_host_config(undefined) ->
|
|
||||||
#'DockerHostConfig'{};
|
|
||||||
ensure_host_config(HostConfig = #'DockerHostConfig'{}) ->
|
|
||||||
HostConfig.
|
|
||||||
|
|
||||||
%% 构建最终 JSON Map
|
|
||||||
build_options(#'DockerCreateOptions'{
|
|
||||||
config = #'DockerContainerConfig'{
|
|
||||||
image = Image,
|
|
||||||
cmd = Cmd,
|
|
||||||
entrypoint = Entrypoint,
|
|
||||||
env = Env,
|
|
||||||
labels = Labels,
|
|
||||||
volumes = Volumes,
|
|
||||||
user = User,
|
|
||||||
working_dir = WorkingDir,
|
|
||||||
hostname = Hostname,
|
|
||||||
exposed_ports = ExposedPorts,
|
|
||||||
healthcheck = Healthcheck
|
|
||||||
},
|
|
||||||
host_config = #'DockerHostConfig'{
|
|
||||||
binds = Binds,
|
|
||||||
network_mode = NetworkMode,
|
|
||||||
restart_policy = RestartPolicy,
|
|
||||||
privileged = Privileged,
|
|
||||||
cap_add = CapAdd,
|
|
||||||
cap_drop = CapDrop,
|
|
||||||
devices = Devices,
|
|
||||||
memory = Memory,
|
|
||||||
memory_reservation = MemoryReservation,
|
|
||||||
nano_cpus = NanoCpus,
|
|
||||||
cpu_shares = CpuShares,
|
|
||||||
ulimits = Ulimits,
|
|
||||||
tmpfs = Tmpfs,
|
|
||||||
sysctls = Sysctls,
|
|
||||||
extra_hosts = ExtraHosts
|
|
||||||
},
|
|
||||||
networking_config = #'DockerNetworkingConfig'{
|
|
||||||
endpoints = Endpoints
|
|
||||||
}
|
|
||||||
}) ->
|
|
||||||
#{
|
|
||||||
<<"Image">> => to_binary(Image),
|
|
||||||
<<"Cmd">> => [to_binary(CommandItem) || CommandItem <- Cmd],
|
|
||||||
<<"Entrypoint">> => [to_binary(EntrypointItem) || EntrypointItem <- Entrypoint],
|
|
||||||
<<"Env">> => [to_binary(EnvItem) || EnvItem <- Env],
|
|
||||||
<<"Labels">> => maps:from_list([{to_binary(Key), to_binary(Value)} || {Key, Value} <- Labels]),
|
|
||||||
<<"Volumes">> => build_volumes(Volumes),
|
|
||||||
<<"User">> => to_binary(User),
|
|
||||||
<<"WorkingDir">> => to_binary(WorkingDir),
|
|
||||||
<<"Hostname">> => to_binary(Hostname),
|
|
||||||
<<"ExposedPorts">> => build_expose(ExposedPorts),
|
|
||||||
<<"NetworkingConfig">> => build_networking_config(Endpoints),
|
|
||||||
<<"Healthcheck">> => build_healthcheck(Healthcheck),
|
|
||||||
<<"HostConfig">> => fold_merge([
|
|
||||||
build_binds(Binds),
|
|
||||||
build_network_mode(NetworkMode),
|
|
||||||
build_restart(RestartPolicy),
|
|
||||||
build_privileged(Privileged),
|
|
||||||
build_cap_add_drop(CapAdd, CapDrop),
|
|
||||||
build_devices(Devices),
|
|
||||||
build_resources(Memory, MemoryReservation, NanoCpus, CpuShares),
|
|
||||||
build_ulimits(Ulimits),
|
|
||||||
build_tmpfs(Tmpfs),
|
|
||||||
build_sysctls(Sysctls),
|
|
||||||
build_extra_hosts(ExtraHosts)
|
|
||||||
])
|
|
||||||
}.
|
|
||||||
|
|
||||||
%% 工具函数
|
|
||||||
fold_merge(List) ->
|
|
||||||
lists:foldl(fun maps:merge/2, #{}, List).
|
|
||||||
|
|
||||||
%% --- 构建子字段 ---
|
|
||||||
build_expose(Ports) when is_list(Ports) ->
|
|
||||||
case Ports of
|
|
||||||
[] -> #{};
|
|
||||||
_ ->
|
|
||||||
maps:from_list([{normalize_expose_port(P), #{}} || P <- Ports])
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_volumes(Vols) when is_list(Vols) ->
|
|
||||||
case Vols of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
maps:from_list([{to_binary(Cont), #{}} || Cont <- Vols])
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_binds(Binds) when is_list(Binds) ->
|
|
||||||
case Binds of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
#{<<"Binds">> => [to_binary(Bind) || Bind <- Binds]}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_networking_config(Endpoints) when is_list(Endpoints) ->
|
|
||||||
case Endpoints of
|
|
||||||
[] -> #{};
|
|
||||||
_ ->
|
|
||||||
NetCfg = maps:from_list([{to_binary(Name), #{}} || #'DockerNetworkEndpoint'{name = Name} <- Endpoints]),
|
|
||||||
#{<<"EndpointsConfig">> => NetCfg}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_network_mode(<<>>) ->
|
|
||||||
#{};
|
|
||||||
build_network_mode(NetworkMode) ->
|
|
||||||
#{<<"NetworkMode">> => to_binary(NetworkMode)}.
|
|
||||||
|
|
||||||
build_healthcheck(undefined) ->
|
|
||||||
#{};
|
|
||||||
build_healthcheck(#'Healthcheck'{
|
|
||||||
test = Test,
|
|
||||||
interval_ns = IntervalNs,
|
|
||||||
timeout_ns = TimeoutNs,
|
|
||||||
retries = Retries
|
|
||||||
}) ->
|
|
||||||
#{
|
|
||||||
<<"Test">> => [to_binary(Item) || Item <- Test],
|
|
||||||
<<"Interval">> => IntervalNs,
|
|
||||||
<<"Timeout">> => TimeoutNs,
|
|
||||||
<<"Retries">> => Retries
|
|
||||||
}.
|
|
||||||
|
|
||||||
build_restart(undefined) ->
|
|
||||||
#{};
|
|
||||||
build_restart(#'RestartPolicy'{
|
|
||||||
name = Name,
|
|
||||||
maximum_retry_count = RetryCount
|
|
||||||
}) ->
|
|
||||||
RestartPolicy = #{
|
|
||||||
<<"Name">> => to_binary(Name)
|
|
||||||
},
|
|
||||||
case RetryCount of
|
|
||||||
0 ->
|
|
||||||
#{<<"RestartPolicy">> => RestartPolicy};
|
|
||||||
_ ->
|
|
||||||
#{<<"RestartPolicy">> => RestartPolicy#{
|
|
||||||
<<"MaximumRetryCount">> => RetryCount
|
|
||||||
}}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_privileged(Privileged) ->
|
|
||||||
case to_bool(Privileged) of
|
|
||||||
true ->
|
|
||||||
#{<<"Privileged">> => true};
|
|
||||||
_ ->
|
|
||||||
#{}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_cap_add_drop(Add, Drop) when is_list(Add), is_list(Drop) ->
|
|
||||||
case {Add, Drop} of
|
|
||||||
{[], []} ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
#{
|
|
||||||
<<"CapAdd">> => [to_binary(Item) || Item <- Add],
|
|
||||||
<<"CapDrop">> => [to_binary(Item) || Item <- Drop]
|
|
||||||
}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_devices(Devs) when is_list(Devs) ->
|
|
||||||
case Devs of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
DevObjs = [#{
|
|
||||||
<<"PathOnHost">> => to_binary(HostPath),
|
|
||||||
<<"PathInContainer">> => to_binary(ContainerPath),
|
|
||||||
<<"CgroupPermissions">> => device_permissions(Permissions)
|
|
||||||
} || #'DeviceMapping'{
|
|
||||||
host_path = HostPath,
|
|
||||||
container_path = ContainerPath,
|
|
||||||
cgroup_permissions = Permissions
|
|
||||||
} <- Devs],
|
|
||||||
#{<<"Devices">> => DevObjs}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_resources(MemoryBytes, ReservationBytes, NanoCpus, CpuShares) ->
|
|
||||||
HostConfig0 = #{},
|
|
||||||
HostConfig1 = case MemoryBytes of
|
|
||||||
0 ->
|
|
||||||
HostConfig0;
|
|
||||||
_ ->
|
|
||||||
HostConfig0#{<<"Memory">> => MemoryBytes}
|
|
||||||
end,
|
|
||||||
HostConfig2 = case ReservationBytes of
|
|
||||||
0 ->
|
|
||||||
HostConfig1;
|
|
||||||
_ ->
|
|
||||||
HostConfig1#{<<"MemoryReservation">> => ReservationBytes}
|
|
||||||
end,
|
|
||||||
HostConfig3 = case NanoCpus of
|
|
||||||
0 ->
|
|
||||||
HostConfig2;
|
|
||||||
_ ->
|
|
||||||
HostConfig2#{<<"NanoCpus">> => NanoCpus}
|
|
||||||
end,
|
|
||||||
case CpuShares of
|
|
||||||
0 ->
|
|
||||||
HostConfig3;
|
|
||||||
_ ->
|
|
||||||
HostConfig3#{<<"CpuShares">> => CpuShares}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_ulimits(Ulimits) when is_list(Ulimits) ->
|
|
||||||
case Ulimits of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
#{<<"Ulimits">> => [#{
|
|
||||||
<<"Name">> => to_binary(Name),
|
|
||||||
<<"Soft">> => Soft,
|
|
||||||
<<"Hard">> => Hard
|
|
||||||
} || #'Ulimit'{name = Name, soft = Soft, hard = Hard} <- Ulimits]}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_sysctls(Sysctls) when is_list(Sysctls) ->
|
|
||||||
case Sysctls of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
#{<<"Sysctls">> => maps:from_list([{to_binary(Key), to_binary(Value)} || {Key, Value} <- Sysctls])}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_tmpfs(Tmpfs) when is_list(Tmpfs) ->
|
|
||||||
case Tmpfs of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
#{<<"Tmpfs">> => maps:from_list([{to_binary(Path), to_binary(Options)} || {Path, Options} <- Tmpfs])}
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_extra_hosts(Hosts) when is_list(Hosts) ->
|
|
||||||
case Hosts of
|
|
||||||
[] ->
|
|
||||||
#{};
|
|
||||||
_ ->
|
|
||||||
#{<<"ExtraHosts">> => [to_binary(Host) || Host <- Hosts]}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec display_options(Options :: map()) -> no_return().
|
-spec display_options(Options :: map()) -> no_return().
|
||||||
display_options(Options) when is_map(Options) ->
|
display_options(Options) when is_map(Options) ->
|
||||||
logger:debug("deploy options: ~p", [jiffy:encode(Options, [force_utf8])]),
|
logger:debug("deploy options: ~p", [jiffy:encode(Options, [force_utf8])]),
|
||||||
lists:foreach(fun({K, V}) -> logger:debug("~p => ~p", [K, V]) end, maps:to_list(Options)).
|
lists:foreach(fun({K, V}) -> logger:debug("~p => ~p", [K, V]) end, maps:to_list(Options)).
|
||||||
|
|
||||||
normalize_expose_port(#'DockerExposedPort'{container_port = Port, protocol = Protocol}) ->
|
|
||||||
PortBin = integer_to_binary(Port),
|
|
||||||
ProtocolBin = to_binary(Protocol),
|
|
||||||
case ProtocolBin of
|
|
||||||
<<>> ->
|
|
||||||
<<PortBin/binary, "/tcp">>;
|
|
||||||
<<"tcp">> ->
|
|
||||||
<<PortBin/binary, "/tcp">>;
|
|
||||||
_ ->
|
|
||||||
<<PortBin/binary, "/", ProtocolBin/binary>>
|
|
||||||
end.
|
|
||||||
|
|
||||||
device_permissions(<<>>) ->
|
|
||||||
<<"rwm">>;
|
|
||||||
device_permissions(Permissions) ->
|
|
||||||
to_binary(Permissions).
|
|
||||||
|
|
||||||
add_unique_front([], List) ->
|
|
||||||
List;
|
|
||||||
add_unique_front([Item | Rest], List) ->
|
|
||||||
NList = case lists:member(Item, List) of
|
|
||||||
true -> List;
|
|
||||||
false -> [Item | List]
|
|
||||||
end,
|
|
||||||
add_unique_front(Rest, NList).
|
|
||||||
|
|
||||||
build_stop_container_url(ContainerName, 0) ->
|
build_stop_container_url(ContainerName, 0) ->
|
||||||
lists:flatten(io_lib:format("/containers/~s/stop", [binary_to_list(ContainerName)]));
|
lists:flatten(io_lib:format("/containers/~s/stop", [binary_to_list(ContainerName)]));
|
||||||
build_stop_container_url(ContainerName, TimeoutSeconds) ->
|
build_stop_container_url(ContainerName, TimeoutSeconds) ->
|
||||||
@ -558,17 +255,3 @@ boolean_to_query_value(true) ->
|
|||||||
"true";
|
"true";
|
||||||
boolean_to_query_value(false) ->
|
boolean_to_query_value(false) ->
|
||||||
"false".
|
"false".
|
||||||
|
|
||||||
to_binary(Value) when is_binary(Value) ->
|
|
||||||
Value;
|
|
||||||
to_binary(Value) when is_list(Value) ->
|
|
||||||
unicode:characters_to_binary(Value).
|
|
||||||
|
|
||||||
to_bool(true) ->
|
|
||||||
true;
|
|
||||||
to_bool(1) ->
|
|
||||||
true;
|
|
||||||
to_bool(false) ->
|
|
||||||
false;
|
|
||||||
to_bool(0) ->
|
|
||||||
false.
|
|
||||||
|
|||||||
369
src/docker/docker_container_builder.erl
Normal file
369
src/docker/docker_container_builder.erl
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author anlicheng
|
||||||
|
%%% @copyright (C) 2025, <COMPANY>
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 21. 4月 2026 15:58
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(docker_container_builder).
|
||||||
|
-author("anlicheng").
|
||||||
|
-include("message_pb.hrl").
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([build_options/3]).
|
||||||
|
|
||||||
|
-spec build_options(ContainerName :: binary(), ContainerDir :: string(),
|
||||||
|
Create0 :: message_pb:'DockerCreateOptions'() | undefined) -> map().
|
||||||
|
build_options(ContainerName, ContainerDir, Create0) when is_binary(ContainerName), is_list(ContainerDir) ->
|
||||||
|
ConfigFile = list_to_binary(docker_helper:get_config_file(ContainerDir)),
|
||||||
|
Create = patch_create_options(ContainerName, ConfigFile, Create0),
|
||||||
|
build_create_options(Create).
|
||||||
|
|
||||||
|
-spec patch_create_options(ContainerName :: binary(), ConfigFile :: binary(),
|
||||||
|
Create0 :: message_pb:'DockerCreateOptions'() | undefined) -> message_pb:'DockerCreateOptions'().
|
||||||
|
patch_create_options(ContainerName, ConfigFile, undefined) ->
|
||||||
|
patch_create_options(ContainerName, ConfigFile, #'DockerCreateOptions'{});
|
||||||
|
patch_create_options(ContainerName, ConfigFile, Create = #'DockerCreateOptions'{
|
||||||
|
config = Config0,
|
||||||
|
host_config = HostConfig0
|
||||||
|
}) when is_binary(ContainerName), is_binary(ConfigFile) ->
|
||||||
|
Config = patch_container_config(ContainerName, ensure_container_config(Config0)),
|
||||||
|
HostConfig = patch_host_config(ConfigFile, ensure_host_config(HostConfig0)),
|
||||||
|
Create#'DockerCreateOptions'{config = Config, host_config = HostConfig}.
|
||||||
|
|
||||||
|
-spec patch_container_config(ContainerName :: binary(),
|
||||||
|
Config :: message_pb:'DockerContainerConfig'()) -> message_pb:'DockerContainerConfig'().
|
||||||
|
patch_container_config(ContainerName, Config = #'DockerContainerConfig'{env = Env0, volumes = Volumes0})
|
||||||
|
when is_binary(ContainerName) ->
|
||||||
|
ConfigVolume = <<"/usr/local/etc/service.conf">>,
|
||||||
|
Envs0 = [<<"CONTAINER_NAME=", ContainerName/binary>>],
|
||||||
|
Envs = add_unique_front(Envs0, [to_binary(EnvItem) || EnvItem <- Env0]),
|
||||||
|
Volumes = add_unique_front([ConfigVolume], [to_binary(Volume) || Volume <- Volumes0]),
|
||||||
|
Config#'DockerContainerConfig'{env = Envs, volumes = Volumes}.
|
||||||
|
|
||||||
|
-spec patch_host_config(ConfigFile :: binary(),
|
||||||
|
HostConfig :: message_pb:'DockerHostConfig'()) -> message_pb:'DockerHostConfig'().
|
||||||
|
patch_host_config(ConfigFile, HostConfig = #'DockerHostConfig'{binds = Binds0}) when is_binary(ConfigFile) ->
|
||||||
|
ConfigBind = <<ConfigFile/binary, ":/usr/local/etc/service.conf">>,
|
||||||
|
Binds = add_unique_front([ConfigBind], [to_binary(Bind) || Bind <- Binds0]),
|
||||||
|
HostConfig#'DockerHostConfig'{binds = Binds}.
|
||||||
|
|
||||||
|
-spec ensure_container_config(Config :: message_pb:'DockerContainerConfig'() | undefined) ->
|
||||||
|
message_pb:'DockerContainerConfig'().
|
||||||
|
ensure_container_config(undefined) ->
|
||||||
|
#'DockerContainerConfig'{};
|
||||||
|
ensure_container_config(Config = #'DockerContainerConfig'{}) ->
|
||||||
|
Config.
|
||||||
|
|
||||||
|
-spec ensure_host_config(HostConfig :: message_pb:'DockerHostConfig'() | undefined) ->
|
||||||
|
message_pb:'DockerHostConfig'().
|
||||||
|
ensure_host_config(undefined) ->
|
||||||
|
#'DockerHostConfig'{};
|
||||||
|
ensure_host_config(HostConfig = #'DockerHostConfig'{}) ->
|
||||||
|
HostConfig.
|
||||||
|
|
||||||
|
-spec build_create_options(Create :: message_pb:'DockerCreateOptions'()) -> map().
|
||||||
|
build_create_options(#'DockerCreateOptions'{
|
||||||
|
config = #'DockerContainerConfig'{
|
||||||
|
image = Image,
|
||||||
|
cmd = Cmd,
|
||||||
|
entrypoint = Entrypoint,
|
||||||
|
env = Env,
|
||||||
|
labels = Labels,
|
||||||
|
volumes = Volumes,
|
||||||
|
user = User,
|
||||||
|
working_dir = WorkingDir,
|
||||||
|
hostname = Hostname,
|
||||||
|
exposed_ports = ExposedPorts,
|
||||||
|
healthcheck = Healthcheck
|
||||||
|
},
|
||||||
|
host_config = #'DockerHostConfig'{
|
||||||
|
binds = Binds,
|
||||||
|
network_mode = NetworkMode,
|
||||||
|
restart_policy = RestartPolicy,
|
||||||
|
privileged = Privileged,
|
||||||
|
cap_add = CapAdd,
|
||||||
|
cap_drop = CapDrop,
|
||||||
|
devices = Devices,
|
||||||
|
memory = Memory,
|
||||||
|
memory_reservation = MemoryReservation,
|
||||||
|
nano_cpus = NanoCpus,
|
||||||
|
cpu_shares = CpuShares,
|
||||||
|
ulimits = Ulimits,
|
||||||
|
tmpfs = Tmpfs,
|
||||||
|
sysctls = Sysctls,
|
||||||
|
extra_hosts = ExtraHosts
|
||||||
|
},
|
||||||
|
networking_config = #'DockerNetworkingConfig'{
|
||||||
|
endpoints = Endpoints
|
||||||
|
}
|
||||||
|
}) ->
|
||||||
|
#{
|
||||||
|
<<"Image">> => to_binary(Image),
|
||||||
|
<<"Cmd">> => [to_binary(CommandItem) || CommandItem <- Cmd],
|
||||||
|
<<"Entrypoint">> => [to_binary(EntrypointItem) || EntrypointItem <- Entrypoint],
|
||||||
|
<<"Env">> => [to_binary(EnvItem) || EnvItem <- Env],
|
||||||
|
<<"Labels">> => maps:from_list([{to_binary(Key), to_binary(Value)} || {Key, Value} <- Labels]),
|
||||||
|
<<"Volumes">> => build_volumes(Volumes),
|
||||||
|
<<"User">> => to_binary(User),
|
||||||
|
<<"WorkingDir">> => to_binary(WorkingDir),
|
||||||
|
<<"Hostname">> => to_binary(Hostname),
|
||||||
|
<<"ExposedPorts">> => build_expose(ExposedPorts),
|
||||||
|
<<"NetworkingConfig">> => build_networking_config(Endpoints),
|
||||||
|
<<"Healthcheck">> => build_healthcheck(Healthcheck),
|
||||||
|
<<"HostConfig">> => fold_merge([
|
||||||
|
build_binds(Binds),
|
||||||
|
build_network_mode(NetworkMode),
|
||||||
|
build_restart(RestartPolicy),
|
||||||
|
build_privileged(Privileged),
|
||||||
|
build_cap_add_drop(CapAdd, CapDrop),
|
||||||
|
build_devices(Devices),
|
||||||
|
build_resources(Memory, MemoryReservation, NanoCpus, CpuShares),
|
||||||
|
build_ulimits(Ulimits),
|
||||||
|
build_tmpfs(Tmpfs),
|
||||||
|
build_sysctls(Sysctls),
|
||||||
|
build_extra_hosts(ExtraHosts)
|
||||||
|
])
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec fold_merge(List :: [map()]) -> map().
|
||||||
|
fold_merge(List) ->
|
||||||
|
lists:foldl(fun maps:merge/2, #{}, List).
|
||||||
|
|
||||||
|
-spec build_expose(Ports :: [message_pb:'DockerExposedPort'()]) -> map().
|
||||||
|
build_expose(Ports) when is_list(Ports) ->
|
||||||
|
case Ports of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
maps:from_list([{normalize_expose_port(Port), #{}} || Port <- Ports])
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_volumes(Volumes :: [binary() | list() | atom()]) -> map().
|
||||||
|
build_volumes(Volumes) when is_list(Volumes) ->
|
||||||
|
case Volumes of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
maps:from_list([{to_binary(ContainerPath), #{}} || ContainerPath <- Volumes])
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_binds(Binds :: [binary() | list() | atom()]) -> map().
|
||||||
|
build_binds(Binds) when is_list(Binds) ->
|
||||||
|
case Binds of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
#{<<"Binds">> => [to_binary(Bind) || Bind <- Binds]}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_networking_config(Endpoints :: [message_pb:'DockerNetworkEndpoint'()]) -> map().
|
||||||
|
build_networking_config(Endpoints) when is_list(Endpoints) ->
|
||||||
|
case Endpoints of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
NetCfg = maps:from_list([{to_binary(Name), #{}} || #'DockerNetworkEndpoint'{name = Name} <- Endpoints]),
|
||||||
|
#{<<"EndpointsConfig">> => NetCfg}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_network_mode(NetworkMode :: binary() | list() | atom()) -> map().
|
||||||
|
build_network_mode(<<>>) ->
|
||||||
|
#{};
|
||||||
|
build_network_mode(NetworkMode) ->
|
||||||
|
#{<<"NetworkMode">> => to_binary(NetworkMode)}.
|
||||||
|
|
||||||
|
-spec build_healthcheck(Healthcheck :: message_pb:'Healthcheck'() | undefined) -> map().
|
||||||
|
build_healthcheck(undefined) ->
|
||||||
|
#{};
|
||||||
|
build_healthcheck(#'Healthcheck'{
|
||||||
|
test = Test,
|
||||||
|
interval_ns = IntervalNs,
|
||||||
|
timeout_ns = TimeoutNs,
|
||||||
|
retries = Retries
|
||||||
|
}) ->
|
||||||
|
#{
|
||||||
|
<<"Test">> => [to_binary(Item) || Item <- Test],
|
||||||
|
<<"Interval">> => IntervalNs,
|
||||||
|
<<"Timeout">> => TimeoutNs,
|
||||||
|
<<"Retries">> => Retries
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec build_restart(RestartPolicy :: message_pb:'RestartPolicy'() | undefined) -> map().
|
||||||
|
build_restart(undefined) ->
|
||||||
|
#{};
|
||||||
|
build_restart(#'RestartPolicy'{
|
||||||
|
name = Name,
|
||||||
|
maximum_retry_count = RetryCount
|
||||||
|
}) ->
|
||||||
|
RestartPolicy = #{
|
||||||
|
<<"Name">> => to_binary(Name)
|
||||||
|
},
|
||||||
|
case RetryCount of
|
||||||
|
0 ->
|
||||||
|
#{<<"RestartPolicy">> => RestartPolicy};
|
||||||
|
_ ->
|
||||||
|
#{<<"RestartPolicy">> => RestartPolicy#{
|
||||||
|
<<"MaximumRetryCount">> => RetryCount
|
||||||
|
}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_privileged(Privileged :: any()) -> map().
|
||||||
|
build_privileged(Privileged) ->
|
||||||
|
case to_bool(Privileged) of
|
||||||
|
true ->
|
||||||
|
#{<<"Privileged">> => true};
|
||||||
|
_ ->
|
||||||
|
#{}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_cap_add_drop(Add :: [binary() | list() | atom()], Drop :: [binary() | list() | atom()]) -> map().
|
||||||
|
build_cap_add_drop(Add, Drop) when is_list(Add), is_list(Drop) ->
|
||||||
|
case {Add, Drop} of
|
||||||
|
{[], []} ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
#{
|
||||||
|
<<"CapAdd">> => [to_binary(Item) || Item <- Add],
|
||||||
|
<<"CapDrop">> => [to_binary(Item) || Item <- Drop]
|
||||||
|
}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_devices(Devices :: [message_pb:'DeviceMapping'()]) -> map().
|
||||||
|
build_devices(Devices) when is_list(Devices) ->
|
||||||
|
case Devices of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
DevObjs = [#{
|
||||||
|
<<"PathOnHost">> => to_binary(HostPath),
|
||||||
|
<<"PathInContainer">> => to_binary(ContainerPath),
|
||||||
|
<<"CgroupPermissions">> => device_permissions(Permissions)
|
||||||
|
} || #'DeviceMapping'{
|
||||||
|
host_path = HostPath,
|
||||||
|
container_path = ContainerPath,
|
||||||
|
cgroup_permissions = Permissions
|
||||||
|
} <- Devices],
|
||||||
|
#{<<"Devices">> => DevObjs}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_resources(MemoryBytes :: integer(), ReservationBytes :: integer(),
|
||||||
|
NanoCpus :: integer(), CpuShares :: integer()) -> map().
|
||||||
|
build_resources(MemoryBytes, ReservationBytes, NanoCpus, CpuShares) ->
|
||||||
|
HostConfig0 = #{},
|
||||||
|
HostConfig1 = case MemoryBytes of
|
||||||
|
0 ->
|
||||||
|
HostConfig0;
|
||||||
|
_ ->
|
||||||
|
HostConfig0#{<<"Memory">> => MemoryBytes}
|
||||||
|
end,
|
||||||
|
HostConfig2 = case ReservationBytes of
|
||||||
|
0 ->
|
||||||
|
HostConfig1;
|
||||||
|
_ ->
|
||||||
|
HostConfig1#{<<"MemoryReservation">> => ReservationBytes}
|
||||||
|
end,
|
||||||
|
HostConfig3 = case NanoCpus of
|
||||||
|
0 ->
|
||||||
|
HostConfig2;
|
||||||
|
_ ->
|
||||||
|
HostConfig2#{<<"NanoCpus">> => NanoCpus}
|
||||||
|
end,
|
||||||
|
case CpuShares of
|
||||||
|
0 ->
|
||||||
|
HostConfig3;
|
||||||
|
_ ->
|
||||||
|
HostConfig3#{<<"CpuShares">> => CpuShares}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_ulimits(Ulimits :: [message_pb:'Ulimit'()]) -> map().
|
||||||
|
build_ulimits(Ulimits) when is_list(Ulimits) ->
|
||||||
|
case Ulimits of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
#{<<"Ulimits">> => [#{
|
||||||
|
<<"Name">> => to_binary(Name),
|
||||||
|
<<"Soft">> => Soft,
|
||||||
|
<<"Hard">> => Hard
|
||||||
|
} || #'Ulimit'{name = Name, soft = Soft, hard = Hard} <- Ulimits]}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_sysctls(Sysctls :: [{binary() | list() | atom(), binary() | list() | atom()}]) -> map().
|
||||||
|
build_sysctls(Sysctls) when is_list(Sysctls) ->
|
||||||
|
case Sysctls of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
#{<<"Sysctls">> => maps:from_list([{to_binary(Key), to_binary(Value)} || {Key, Value} <- Sysctls])}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_tmpfs(Tmpfs :: [{binary() | list() | atom(), binary() | list() | atom()}]) -> map().
|
||||||
|
build_tmpfs(Tmpfs) when is_list(Tmpfs) ->
|
||||||
|
case Tmpfs of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
#{<<"Tmpfs">> => maps:from_list([{to_binary(Path), to_binary(Options)} || {Path, Options} <- Tmpfs])}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec build_extra_hosts(Hosts :: [binary() | list() | atom()]) -> map().
|
||||||
|
build_extra_hosts(Hosts) when is_list(Hosts) ->
|
||||||
|
case Hosts of
|
||||||
|
[] ->
|
||||||
|
#{};
|
||||||
|
_ ->
|
||||||
|
#{<<"ExtraHosts">> => [to_binary(Host) || Host <- Hosts]}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec normalize_expose_port(Port :: message_pb:'DockerExposedPort'()) -> binary().
|
||||||
|
normalize_expose_port(#'DockerExposedPort'{container_port = Port, protocol = Protocol}) ->
|
||||||
|
PortBin = integer_to_binary(Port),
|
||||||
|
ProtocolBin = to_binary(Protocol),
|
||||||
|
case ProtocolBin of
|
||||||
|
<<>> ->
|
||||||
|
<<PortBin/binary, "/tcp">>;
|
||||||
|
<<"tcp">> ->
|
||||||
|
<<PortBin/binary, "/tcp">>;
|
||||||
|
_ ->
|
||||||
|
<<PortBin/binary, "/", ProtocolBin/binary>>
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec device_permissions(Permissions :: binary() | list() | atom()) -> binary().
|
||||||
|
device_permissions(<<>>) ->
|
||||||
|
<<"rwm">>;
|
||||||
|
device_permissions(Permissions) ->
|
||||||
|
to_binary(Permissions).
|
||||||
|
|
||||||
|
-spec add_unique_front(Items :: [binary()], List :: [binary()]) -> [binary()].
|
||||||
|
add_unique_front([], List) ->
|
||||||
|
List;
|
||||||
|
add_unique_front([Item | Rest], List) ->
|
||||||
|
NList = case lists:member(Item, List) of
|
||||||
|
true ->
|
||||||
|
List;
|
||||||
|
false ->
|
||||||
|
[Item | List]
|
||||||
|
end,
|
||||||
|
add_unique_front(Rest, NList).
|
||||||
|
|
||||||
|
-spec to_binary(Value :: binary() | list() | atom() | any()) -> binary().
|
||||||
|
to_binary(Value) when is_binary(Value) ->
|
||||||
|
Value;
|
||||||
|
to_binary(Value) when is_list(Value) ->
|
||||||
|
list_to_binary(Value);
|
||||||
|
to_binary(Value) when is_atom(Value) ->
|
||||||
|
atom_to_binary(Value, utf8);
|
||||||
|
to_binary(Value) ->
|
||||||
|
iolist_to_binary(io_lib:format("~p", [Value])).
|
||||||
|
|
||||||
|
-spec to_bool(Value :: any()) -> boolean().
|
||||||
|
to_bool(true) ->
|
||||||
|
true;
|
||||||
|
to_bool(<<"true">>) ->
|
||||||
|
true;
|
||||||
|
to_bool("true") ->
|
||||||
|
true;
|
||||||
|
to_bool(_) ->
|
||||||
|
false.
|
||||||
Loading…
x
Reference in New Issue
Block a user