%%%------------------------------------------------------------------- %%% @author anlicheng %%% @copyright (C) 2025, %%% @doc %%% %%% @end %%% Created : 17. 9月 2025 14:50 %%%------------------------------------------------------------------- -module(docker_helper). -author("anlicheng"). %% API -export([root_dir/0]). -export([ensure_container_dir/2, ensure_container_dir/3, get_container_dir/2, get_config_file/1]). -export([update_container_config/2]). -spec root_dir() -> {ok, string()} | undefined. root_dir() -> case application:get_env(docker, root_dir) of {ok, RootDir} -> {ok, RootDir}; undefined -> application:get_env(efka, root_dir) end. -spec ensure_container_dir(RootDir :: string(), ContainerName :: binary()) -> {ok, ServerRootDir :: string()}. ensure_container_dir(RootDir, ContainerName) when is_list(RootDir), is_binary(ContainerName) -> ensure_container_dir(RootDir, ContainerName, <<>>). -spec ensure_container_dir(RootDir :: string(), ContainerName :: binary(), ContainerDir :: binary()) -> {ok, ServerRootDir :: string()}. ensure_container_dir(RootDir, ContainerName, ContainerDir) when is_list(RootDir), is_binary(ContainerName), is_binary(ContainerDir) -> PointerDir = default_container_dir(RootDir, ContainerName), PointerFile = container_dir_pointer_file(PointerDir), ActualContainerDir = resolve_container_dir(RootDir, ContainerName, ContainerDir), ok = filelib:ensure_dir(PointerDir), ok = filelib:ensure_dir(ActualContainerDir), ok = file:write_file(PointerFile, unicode:characters_to_binary(ActualContainerDir), [write]), {ok, ActualContainerDir}. -spec get_config_file(ContainerDir :: string()) -> ConfigFile :: string(). get_config_file(ContainerDir) when is_list(ContainerDir) -> %% 根目录 ContainerDir ++ "service.conf". -spec get_container_dir(RootDir :: string(), ContainerName :: binary()) -> {ok, ServerRootDir :: string()} | error. get_container_dir(RootDir, ContainerName) when is_list(RootDir), is_binary(ContainerName) -> ContainerRootDir = default_container_dir(RootDir, ContainerName), PointerFile = container_dir_pointer_file(ContainerRootDir), ResolvedContainerDir = case file:read_file(PointerFile) of {ok, ActualContainerDirBin} -> normalize_container_dir(binary_to_list(ActualContainerDirBin)); {error, _} -> ContainerRootDir end, case filelib:is_dir(ResolvedContainerDir) of true -> {ok, ResolvedContainerDir}; false -> error end. -spec update_container_config(binary(), binary()) -> ok | {error, binary()}. update_container_config(ContainerName, Config) when is_binary(ContainerName), is_binary(Config) -> {ok, RootDir} = root_dir(), case get_container_dir(RootDir, ContainerName) of {ok, ContainerDir} -> ConfigFile = get_config_file(ContainerDir), case file:write_file(ConfigFile, Config, [write, binary]) of ok -> logger:warning("[efka_iot_client] write config file: ~p success", [ConfigFile]), ok; {error, Reason} -> logger:warning("[efka_iot_client] write config file: ~p, get error: ~p", [ConfigFile, Reason]), {error, <<"write config failed">>} end; error -> {error, <<"error">>} end. -spec default_container_dir(RootDir :: string(), ContainerName :: binary()) -> string(). default_container_dir(RootDir, ContainerName) -> normalize_container_dir(RootDir ++ "/" ++ binary_to_list(ContainerName)). -spec container_dir_pointer_file(ContainerDir :: string()) -> string(). container_dir_pointer_file(ContainerDir) -> ContainerDir ++ ".container_dir". -spec resolve_container_dir(RootDir :: string(), ContainerName :: binary(), ContainerDir :: binary()) -> string(). resolve_container_dir(RootDir, ContainerName, <<>>) -> default_container_dir(RootDir, ContainerName); resolve_container_dir(_RootDir, _ContainerName, ContainerDir) -> normalize_container_dir(binary_to_list(ContainerDir)). -spec normalize_container_dir(ContainerDir :: string()) -> string(). normalize_container_dir(ContainerDir) -> case lists:last(ContainerDir) of $/ -> ContainerDir; _ -> ContainerDir ++ "/" end.