diff --git a/Dockerfile b/Dockerfile index 7b08ba0..3f7d5b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -88,7 +88,7 @@ COPY --from=releaser /opt/rel . # Alias remote shell script under a single command RUN touch ~/.profile && \ - printf "%s\n" "alias remshell='/opt/braidnet/bin/braidnet remote_console'" \ + printf "%s\n" 'alias remshell='\''/opt/braidnet/erts-*/bin/erl -boot $(find /opt/braidnet/releases -type f -name start_clean.boot | sed "s/\.boot$//") -setcookie cookie -proto_dist inet6_tls -ssl_dist_optfile lib/braidnet-*/priv/prod.ssl_dist_opts.rel -sname console -remsh braidnet@$(hostname)'\''' \ >> ~/.profile EXPOSE 80/tcp diff --git a/config/container.config.src b/config/container.config.src index a0b4481..72fd7ad 100644 --- a/config/container.config.src +++ b/config/container.config.src @@ -1,7 +1,7 @@ [ {braidnet, [ {rest_api_token, <<"${AUTH_TOKEN}">>}, - {braidcert_url, "https://braidcert.fly.dev/csr/"}, + {braidcert_url, "${BRAIDCERT_CSR_URL:-https://braidcert.fly.dev/csr/}"}, {braidcert_key, "${BRAIDCERT_KEY}"}, {docker_trust, false} % defaults to true ]}, diff --git a/hooks/pre_start b/hooks/pre_start index d5bfd57..5740cac 100755 --- a/hooks/pre_start +++ b/hooks/pre_start @@ -1,14 +1,28 @@ #!/bin/ash +check_error() { + local code="${1:-1}" + local message="${2:-"Unknown Error"}" + if [ "${code}" -ne 0 ]; then + echo "Error: ${message} (${code})" >&2 + exit ${code} + fi +} + +BRAIDCERT_DOMAIN=$(echo "$BRAIDCERT_CSR_URL" | sed -e 's|https://||' -e 's|/.*||') +BRAIDCERT_CA_URL="https://${BRAIDCERT_DOMAIN}/ca" # Get the Braidcert CA: -curl \ - -X GET https://braidcert.fly.dev/ca \ +curl --fail \ + -X GET "${BRAIDCERT_CA_URL}" \ -H "Authorization: Bearer ${BRAIDCERT_KEY}" \ >> certs/braidcert.CA.pem +check_error $? "Failed to get BraidCert CA" + # Generate a private key for the signing request: openssl genrsa -out certs/braidnet.key 4096 +check_error $? "Failed to generate private key" # Generate a certificate signing request for this Braidnet instance: openssl req \ @@ -17,14 +31,16 @@ openssl req \ -key certs/braidnet.key \ -addext subjectAltName=DNS.0:"${FLY_MACHINE_ID}" \ -out certs/braidnet.csr +check_error $? "Failed to generate CSR" # Send the request to Braidcert and save the resulting certificate: -curl \ - -X PUT https://braidcert.fly.dev/csr/"${FLY_MACHINE_ID}" \ +curl --fail \ + -X PUT "${BRAIDCERT_CSR_URL}/${FLY_MACHINE_ID}" \ -H "Authorization: Bearer ${BRAIDCERT_KEY}" \ -H "Content-Type: application/octet-stream" \ --data-binary "@certs/braidnet.csr" \ >> certs/braidnet.pem +check_error $? "Failed to get BraidCert certificate" # Remove the request file: -rm certs/braidnet.csr +rm -f certs/braidnet.csr diff --git a/rebar.config b/rebar.config index 200c7fe..448351c 100644 --- a/rebar.config +++ b/rebar.config @@ -28,7 +28,7 @@ {profiles, [ {container, [ {relx, [ - {release, {braidnet, "0.1.0"}, [braidnet]}, + {release, {braidnet, "0.1.0"}, [braidnet, runtime_tools]}, {sys_config_src, "./config/container.config.src"}, {vm_args, "./config/container.vm.args.src"}, {mode, prod}, diff --git a/rebar.lock b/rebar.lock index e8ef30a..3d594b7 100644 --- a/rebar.lock +++ b/rebar.lock @@ -2,7 +2,7 @@ [{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.10.0">>},0}, {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.12.1">>},1}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.1">>},0}, - {<<"quickrand">>,{pkg,<<"quickrand">>,<<"2.0.5">>},1}, + {<<"quickrand">>,{pkg,<<"quickrand">>,<<"2.0.7">>},1}, {<<"ranch">>,{pkg,<<"ranch">>,<<"1.8.0">>},1}, {<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.5">>},0}]}. [ @@ -10,14 +10,14 @@ {<<"cowboy">>, <<"FF9FFEFF91DAE4AE270DD975642997AFE2A1179D94B1887863E43F681A203E26">>}, {<<"cowlib">>, <<"A9FA9A625F1D2025FE6B462CB865881329B5CAFF8F1854D1CBC9F9533F00E1E1">>}, {<<"jiffy">>, <<"ACA10F47AA91697BF24AB9582C74E00E8E95474C7EF9F76D4F1A338D0F5DE21B">>}, - {<<"quickrand">>, <<"06FCAD85CB47D5C85C51D6BC9C84A082501BA098A89D64AD0A2F69599E034C04">>}, + {<<"quickrand">>, <<"D2BD76676A446E6A058D678444B7FDA1387B813710D1AF6D6E29BB92186C8820">>}, {<<"ranch">>, <<"8C7A100A139FD57F17327B6413E4167AC559FBC04CA7448E9BE9057311597A1D">>}, {<<"uuid">>, <<"60FAEEB7EDFD40847ED13CB0DD1044BAABE4E79A00C0CA9C4D13A073914B1016">>}]}, {pkg_hash_ext,[ {<<"cowboy">>, <<"3AFDCCB7183CC6F143CB14D3CF51FA00E53DB9EC80CDCD525482F5E99BC41D6B">>}, {<<"cowlib">>, <<"163B73F6367A7341B33C794C4E88E7DBFE6498AC42DCD69EF44C5BC5507C8DB0">>}, {<<"jiffy">>, <<"62E1F0581C3C19C33A725C781DFA88410D8BFF1BBAFC3885A2552286B4785C4C">>}, - {<<"quickrand">>, <<"252CF0493570EBF1A58985CB71990982CDDCD4396B6427F1E10CF58924C1C052">>}, + {<<"quickrand">>, <<"B8ACBF89A224BC217C3070CA8BEBC6EB236DBE7F9767993B274084EA044D35F0">>}, {<<"ranch">>, <<"49FBCFD3682FAB1F5D109351B61257676DA1A2FDBE295904176D5E521A2DDFE5">>}, {<<"uuid">>, <<"E54373262CA88401689277947C54B95E9ECBC977BD5C57C9DD44AD9DA278E360">>}]} ]. diff --git a/src/braidnet.erl b/src/braidnet.erl index 576aad9..d183e84 100644 --- a/src/braidnet.erl +++ b/src/braidnet.erl @@ -74,13 +74,26 @@ list() -> logs(CID) -> braidnet_orchestrator:logs(CID). -rpc(CID, M, F, A) -> +rpc(CID, M, F, A) + when is_atom(M), is_atom(F), is_list(A) orelse A =:= undefined -> + M2 = base64:encode(term_to_binary(M)), + F2 = base64:encode(term_to_binary(F)), + A2 = case A =:= undefined of + true -> undefined; + false -> base64:encode(term_to_binary(A)) + end, + case rpc(CID, M2, F2, A2) of + {ok, Result} -> binary_to_term(base64:decode(Result)); + {error, _Reason} = Error -> Error + end; +rpc(CID, M, F, A) + when is_binary(M), is_binary(F), is_binary(A) orelse A =:= undefined -> case braidnet_orchestrator:get_ws_pid(CID) of undefined -> - base64:encode("no_connection"); + {error, no_connection}; Pid -> Params = #{m => M, f => F, a => A}, - braidnet_braidnode_api:request(Pid, self(), rpc, Params) + braidnet_braidnode_api:request(Pid, rpc, Params) end. remove_configuration(NodesMap) -> diff --git a/src/braidnet_braid_rest_api.erl b/src/braidnet_braid_rest_api.erl index 35f99e0..6781a3f 100644 --- a/src/braidnet_braid_rest_api.erl +++ b/src/braidnet_braid_rest_api.erl @@ -95,8 +95,14 @@ to_json(#{bindings := #{method := <<"rpc">>}, qs := Qs} = Req, S) -> M = get_qs_entry(<<"m">>, Qs), F = get_qs_entry(<<"f">>, Qs), A = get_qs_entry(<<"args">>, Qs), - Result = braidnet:rpc(CID, M, F, A), - {json_encode(Result), Req, S}. + case braidnet:rpc(CID, M, F, A) of + {ok, Result} -> + {json_encode(Result), Req, S}; + {error, Reason} -> + ErrorMsg = base64:encode(iolist_to_binary(io_lib:format("~p",[Reason]))), + Req2 = cowboy_req:reply(400, #{}, ErrorMsg, Req), + {stop, Req2, S} + end. from_json(#{bindings := #{method := <<"launch">>}} = Req, BraidCfg = S) -> braidnet:launch_configuration(BraidCfg), diff --git a/src/braidnet_braidnode_api.erl b/src/braidnet_braidnode_api.erl index 6dc2847..4bfd9e2 100644 --- a/src/braidnet_braidnode_api.erl +++ b/src/braidnet_braidnode_api.erl @@ -5,8 +5,8 @@ -export([notify/2]). -export([notify/3]). +-export([request/2]). -export([request/3]). --export([request/4]). -behaviour(cowboy_websocket). @@ -31,12 +31,19 @@ notify(Pid, Method) -> notify(Pid, Method, Params) -> Pid ! {notify, Method, Params}. -request(Pid, Caller, Method) -> - request(Pid, Caller, Method, undefined). - -request(Pid, Caller, Method, Params) -> - Pid ! {request, Caller, Method, Params}, - receive {Pid, Msg} -> Caller ! Msg end. +request(Pid, Method) -> + request(Pid, Method, undefined). + +request(Pid, Method, Params) -> + MonRef = erlang:monitor(process, Pid), + Pid ! {request, self(), MonRef, Method, Params}, + receive + {Pid, MonRef, Result} -> + erlang:demonitor(MonRef, [flush]), + Result; + {'DOWN', MonRef, process, Pid, Reason} -> + {error, Reason} + end. %--- WS Callbacks -------------------------------------------------------------- @@ -88,14 +95,14 @@ websocket_handle(_Frame, State) -> websocket_info({notify, Method, Params}, State) -> JsonRPC = braidnet_jsonrpc:notification(Method, Params), {[{binary, JsonRPC}], State}; -websocket_info({request, Caller, Method, undefined}, #state{pending_requests = Map} = S) -> +websocket_info({request, Caller, Ref, Method, undefined}, #state{pending_requests = Map} = S) -> ID = id(), JsonRPC = braidnet_jsonrpc:call(Method, ID), - {[{binary, JsonRPC}], S#state{pending_requests = Map#{ID => Caller}}}; -websocket_info({request, Caller, Method, Params}, #state{pending_requests = Map} = S) -> + {[{binary, JsonRPC}], S#state{pending_requests = Map#{ID => {Caller, Ref}}}}; +websocket_info({request, Caller, Ref, Method, Params}, #state{pending_requests = Map} = S) -> ID = id(), JsonRPC = braidnet_jsonrpc:call(Method, Params, ID), - {[{binary, JsonRPC}], S#state{pending_requests = Map#{ID => Caller}}}; + {[{binary, JsonRPC}], S#state{pending_requests = Map#{ID => {Caller, Ref}}}}; websocket_info(Info, State) -> ?LOG_WARNING("Unexpected info: ~p", [Info]), {ok, State}. @@ -121,13 +128,13 @@ handle_notification(_CID, Method, _Params) -> ?LOG_WARNING("Unhandled jsonrpc notification method ~p",[Method]). handle_response({result, Result, ID}, #state{pending_requests = Preqs} = S) -> - #{ID := Caller} = Preqs, - Caller ! {self(), Result}, + #{ID := {Caller, Ref}} = Preqs, + Caller ! {self(), Ref, {ok, Result}}, S#state{pending_requests = maps:remove(ID, Preqs)}; -handle_response({error, _, _, _, ID} = Error, +handle_response({error, Code, Message, Extra, ID}, #state{pending_requests = Preqs} = S) -> - #{ID := Caller} = Preqs, - Caller ! {self(), Error}, + #{ID := {Caller, Ref}} = Preqs, + Caller ! {self(), Ref, {error, {Code, Message, Extra}}}, S#state{pending_requests = maps:remove(ID, Preqs)}. id() -> uuid:uuid_to_string(uuid:get_v4(), binary_standard).