%%%------------------------------------------------------------------- %%% @author licheng5 %%% @copyright (C) 2020, %%% @doc %%% %%% @end %%% Created : 26. 4月 2020 3:36 下午 %%%------------------------------------------------------------------- -module(container_handler). -author("licheng5"). -include("iot.hrl"). -define(REQ_TIMEOUT, 10000). %% API -export([handle_request/4]). handle_request("GET", "/container/get_all", #{<<"uuid">> := UUID}, _) when is_binary(UUID) -> %% 检查ConfigJson是否是合法的json字符串 case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(-1, <<"host not found">>)}; Pid when is_pid(Pid) -> case iot_host:get_containers(Pid) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, ?REQ_TIMEOUT) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> {ok, 200, iot_util:json_error(-1, Reason)} end end; %% 下发config.json, 微服务接受后,保存服务配置 handle_request("POST", "/container/push_config", _, #{<<"uuid">> := UUID, <<"container_name">> := ContainerName, <<"config">> := Config, <<"timeout">> := Timeout0}) when is_binary(UUID), is_binary(ContainerName), is_binary(Config), is_integer(Timeout0) -> %% 检查ConfigJson是否是合法的json字符串 true = iot_util:is_json(Config), case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(-1, <<"host not found">>)}; Pid when is_pid(Pid) -> Timeout = Timeout0 * 1000, case iot_host:config_container(Pid, ContainerName, Config) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, Timeout) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> {ok, 200, iot_util:json_error(-1, Reason)} end end; %% 部署微服务 handle_request("POST", "/container/deploy", _, #{<<"uuid">> := UUID, <<"task_id">> := TaskId, <<"config">> := Config}) when is_binary(UUID), is_integer(TaskId), is_map(Config) -> case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(404, <<"host not found">>)}; Pid when is_pid(Pid) -> case iot_container_task_sup:ensure_started(UUID, TaskId) of {ok, _TaskPid} -> handle_deploy_container(Pid, UUID, TaskId, Config); {error, Reason} -> {ok, 500, iot_util:json_error(500, reason_to_binary(Reason))} end end; %% 启动服务 handle_request("POST", "/container/start", _, #{<<"uuid">> := UUID, <<"container_name">> := ContainerName}) when is_binary(UUID), is_binary(ContainerName) -> case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(404, <<"host not found">>)}; Pid when is_pid(Pid) -> case iot_host:start_container(Pid, ContainerName) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, ?REQ_TIMEOUT) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> {ok, 200, iot_util:json_error(400, Reason)} end end; %% 停止服务 handle_request("POST", "/container/stop", _, #{<<"uuid">> := UUID, <<"container_name">> := ContainerName}) when is_binary(UUID), is_binary(ContainerName) -> case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(404, <<"host not found">>)}; Pid when is_pid(Pid) -> case iot_host:stop_container(Pid, ContainerName) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, ?REQ_TIMEOUT) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> {ok, 200, iot_util:json_error(400, Reason)} end end; handle_request("POST", "/container/kill", _, #{<<"uuid">> := UUID, <<"container_name">> := ContainerName}) when is_binary(UUID), is_binary(ContainerName) -> case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(404, <<"host not found">>)}; Pid when is_pid(Pid) -> case iot_host:kill_container(Pid, ContainerName) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, ?REQ_TIMEOUT) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> {ok, 200, iot_util:json_error(400, Reason)} end end; %% 删除容器 handle_request("POST", "/container/remove", _, #{<<"uuid">> := UUID, <<"container_name">> := ContainerName}) when is_binary(UUID), is_binary(ContainerName) -> case iot_host:get_pid(UUID) of undefined -> {ok, 200, iot_util:json_error(404, <<"host not found">>)}; Pid when is_pid(Pid) -> case iot_host:remove_container(Pid, ContainerName) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, ?REQ_TIMEOUT) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> {ok, 200, iot_util:json_error(400, Reason)} end end; handle_request(_, Path, _, _) -> Path1 = list_to_binary(Path), {ok, 200, iot_util:json_error(-1, <<"url: ", Path1/binary, " not found">>)}. request_success_response(Result) when is_binary(Result) -> case decode_json_bytes(Result) of {ok, Data} -> iot_util:json_data(Data); error -> iot_util:json_data(Result) end; request_success_response(Result) -> iot_util:json_data(Result). request_error_response(Code, Reason) when is_integer(Code), is_binary(Reason) -> case decode_json_bytes(Reason) of {ok, #{<<"message">> := Message}} when is_binary(Message) -> iot_util:json_error(Code, Message); {ok, Message} when is_binary(Message) -> iot_util:json_error(Code, Message); _ -> iot_util:json_error(Code, Reason) end. -spec request_error_http_response(Reason :: term()) -> {ok, HttpStatus :: 400 | 504, Body :: binary()}. request_error_http_response(Reason) -> HttpStatus = request_error_status(Reason), {ok, HttpStatus, request_error_response(HttpStatus, reason_to_binary(Reason))}. -spec request_error_status(Reason :: term()) -> integer(). request_error_status(timeout) -> 504; request_error_status(<<"timeout">>) -> 504; request_error_status(_) -> 400. -spec reason_to_binary(term()) -> binary(). reason_to_binary(Reason) when is_binary(Reason) -> Reason; reason_to_binary(timeout) -> <<"timeout">>; reason_to_binary(invalid_response) -> <<"invalid response">>; reason_to_binary(Reason) -> unicode:characters_to_binary(io_lib:format("~p", [Reason])). decode_json_bytes(Data) when is_binary(Data) -> case catch json:decode(Data) of {'EXIT', _} -> error; {error, _} -> error; Decoded -> {ok, Decoded} end. -spec handle_deploy_container(pid(), binary(), integer(), map()) -> {ok, 200 | 400 | 504, binary()}. handle_deploy_container(Pid, UUID, TaskId, Config) -> case iot_host:deploy_container(Pid, TaskId, Config) of {ok, Ref} -> case iot_host:await_reply(Pid, Ref, ?REQ_TIMEOUT) of ok -> {ok, 200, request_success_response(<<"ok">>)}; {ok, Result} -> {ok, 200, request_success_response(Result)}; {error, Reason} -> ok = iot_container_task_sup:fail(UUID, TaskId, Reason), request_error_http_response(Reason) end; {error, Reason} when is_binary(Reason) -> ok = iot_container_task_sup:fail(UUID, TaskId, Reason), {ok, 200, iot_util:json_error(400, Reason)} end.