fix builder

This commit is contained in:
anlicheng 2026-04-21 16:14:42 +08:00
parent 72030d55d6
commit ac4fabad1d
2 changed files with 370 additions and 318 deletions

View File

@ -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.

View 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.