MSPF-320: Introduce CAPI copypaste

This commit is contained in:
Dmitry Manik 2017-11-30 20:09:48 +03:00 committed by Andrew Mayorov
parent 7fdb5913ce
commit 79c7768bdf
9 changed files with 313 additions and 8 deletions

View File

@ -62,7 +62,7 @@ start: submodules
devrel: submodules
$(REBAR) release
release: distclean
release: submodules distclean generate
$(REBAR) as prod release
clean:

View File

@ -2,9 +2,16 @@
{description, "Url shortener service"},
{vsn, "0"},
{registered, []},
{mod, {shortener, []}},
{applications, [
kernel,
stdlib
stdlib,
lager,
lager_logstash_formatter,
genlib,
jose,
cowboy_cors,
swag
]},
{env, []},
{modules, []},

View File

@ -8,13 +8,13 @@
%%
-spec start(normal, any()) ->
{ok, pid()} | {error, any()}.
-spec start(normal, any()) -> {ok, pid()} | {error, any()}.
start(_StartType, _StartArgs) ->
shortener_sup:start_link().
-spec stop(any()) ->
ok.
-spec stop(any()) -> ok.
stop(_State) ->
ok.

View File

@ -0,0 +1,47 @@
-module(shortener_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% API
-spec start_link() -> {ok, pid()} | {error, {already_started, pid()}}.
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% Supervisor callbacks
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
%% AuthorizerSpecs = get_authorizer_child_specs(),
{LogicHandler, LogicHandlerSpecs} = get_logic_handler_info(),
SwaggerSpec = shortener_swagger_server:child_spec(LogicHandler),
{ok, {
{one_for_all, 0, 1},
LogicHandlerSpecs ++ [SwaggerSpec]
}}.
%%
%% -spec get_authorizer_child_specs() -> [supervisor:child_spec()].
%% get_authorizer_child_specs() ->
%% Authorizers = genlib_app:env(shortener, authorizers, #{}),
%% [get_authorizer_child_spec(jwt, maps:get(jwt, Authorizers))].
%% -spec get_authorizer_child_spec(Name :: atom(), Options :: #{}) -> supervisor:child_spec().
%% get_authorizer_child_spec(jwt, Options) ->
%% shortener_authorizer_jwt:get_child_spec(Options).
-spec get_logic_handler_info() -> {Handler :: atom(), [Spec :: supervisor:child_spec()] | []} .
get_logic_handler_info() ->
{shortener_handler, []}.

View File

@ -0,0 +1,155 @@
-module(shortener_swagger_server).
-export([child_spec/1]).
-export([request_hook/1]).
-export([response_hook/4]).
-define(APP, shortener).
-define(DEFAULT_ACCEPTORS_POOLSIZE, 100).
-define(DEFAULT_IP_ADDR, "::").
-define(DEFAULT_PORT, 8080).
-define(START_TIME_TAG, processing_start_time).
-spec child_spec(module()) -> supervisor:child_spec().
child_spec(LogicHandler) ->
{Transport, TransportOpts} = get_socket_transport(),
CowboyOpts = get_cowboy_config(LogicHandler),
AcceptorsPool = genlib_app:env(?APP, acceptors_poolsize, ?DEFAULT_ACCEPTORS_POOLSIZE),
ranch:child_spec(?MODULE, AcceptorsPool,
Transport, TransportOpts, cowboy_protocol, CowboyOpts).
get_socket_transport() ->
{ok, IP} = inet:parse_address(genlib_app:env(?APP, ip, ?DEFAULT_IP_ADDR)),
Port = genlib_app:env(?APP, port, ?DEFAULT_PORT),
{ranch_tcp, [{ip, IP}, {port, Port}]}.
get_cowboy_config(LogicHandler) ->
Dispatch = cowboy_router:compile(swag_router:get_paths(LogicHandler)),
[
{env, [
{dispatch, Dispatch},
{cors_policy, shortener_cors_policy}
]},
{middlewares, [
cowboy_router,
cowboy_cors,
cowboy_handler
]},
{onrequest, fun ?MODULE:request_hook/1},
{onresponse, fun ?MODULE:response_hook/4}
].
-spec request_hook(cowboy_req:req()) ->
cowboy_req:req().
request_hook(Req) ->
cowboy_req:set_meta(?START_TIME_TAG, genlib_time:ticks(), Req).
-spec response_hook(cowboy:http_status(), cowboy:http_headers(), iodata(), cowboy_req:req()) ->
cowboy_req:req().
response_hook(Code, Headers, _, Req) ->
try
{Code1, Headers1, Req1} = handle_response(Code, Headers, Req),
_ = log_access(Code1, Headers1, Req1),
Req1
catch
Class:Reason ->
Stack = genlib_format:format_stacktrace(erlang:get_stacktrace(), [newlines]),
_ = lager:warning(
"Response hook failed for: [~p, ~p, ~p]~nwith: ~p:~p~nstacktrace: ~ts",
[Code, Headers, Req, Class, Reason, Stack]
),
Req
end.
handle_response(Code, Headers, Req) when Code >= 500 ->
send_oops_resp(Code, Headers, get_oops_body_safe(Code), Req);
handle_response(Code, Headers, Req) ->
{Code, Headers, Req}.
%% cowboy_req:reply/4 has a faulty spec in case of response body fun.
-dialyzer({[no_contracts, no_fail_call], send_oops_resp/4}).
send_oops_resp(Code, Headers, undefined, Req) ->
{Code, Headers, Req};
send_oops_resp(Code, Headers, File, Req) ->
FileSize = filelib:file_size(File),
F = fun(Socket, Transport) ->
case Transport:sendfile(Socket, File) of
{ok, _} ->
ok;
{error, Error} ->
_ = lager:warning("Failed to send oops body: ~p", [Error]),
ok
end
end,
Headers1 = lists:foldl(
fun({K, V}, Acc) -> lists:keystore(K, 1, Acc, {K, V}) end,
Headers,
[
{<<"content-type">>, <<"text/plain; charset=utf-8">>},
{<<"content-length">>, integer_to_list(FileSize)}
]
),
{ok, Req1} = cowboy_req:reply(Code, Headers1, {FileSize, F}, Req),
{Code, Headers1, Req1}.
get_oops_body_safe(Code) ->
try get_oops_body(Code)
catch
Error:Reason ->
_ = lager:warning("Invalid oops body config for code: ~p. Error: ~p:~p", [Code, Error, Reason]),
undefined
end.
get_oops_body(Code) ->
genlib_map:get(Code, genlib_app:env(?APP, oops_bodies, #{}), undefined).
log_access(Code, Headers, Req) ->
{Method, _} = cowboy_req:method(Req),
{Path, _} = cowboy_req:path(Req),
{ReqLen, _} = cowboy_req:body_length(Req),
{ReqId, _} = cowboy_req:header(<<"x-request-id">>, Req, undefined),
RemAddr = get_remote_addr(Req),
RespLen = get_response_len(Headers),
Duration = get_request_duration(Req),
ReqMeta = [
{remote_addr, RemAddr},
{request_method, Method},
{request_path, Path},
{request_length, ReqLen},
{response_length, RespLen},
{request_time, Duration},
{'http_x-request-id', ReqId},
{status, Code}
],
Meta = orddict:merge(fun(_Key, New, _Old) -> New end, ReqMeta, lager:md()),
%% Call lager:log/5 here directly in order to pass request metadata (fused into
%% lager metadata) without storing it in a process dict via lager:md/1.
lager:log(shortener_access_lager_event, info, Meta, "", []).
get_remote_addr(Req) ->
case swag_handler_api:determine_peer(Req) of
{{ok, #{ip_address := IP}}, _} ->
genlib:to_binary(inet:ntoa(IP));
{_, _} ->
undefined
end.
get_request_duration(Req) ->
case cowboy_req:meta(?START_TIME_TAG, Req) of
{undefined, _} ->
undefined;
{StartTime, _} ->
(genlib_time:ticks() - StartTime) / 1000000
end.
get_response_len(Headers) ->
case lists:keyfind(<<"content-length">>, 1, Headers) of
{_, Len} ->
genlib:to_int(Len);
false ->
undefined
end.

41
config/sys.config Normal file
View File

@ -0,0 +1,41 @@
[
{lager, [
{error_logger_hwm, 600},
{log_root, "/var/log/shortener"},
{handlers, [
{lager_console_backend, debug},
{lager_file_backend, [
{file, "console.json"},
{level, debug},
{formatter, lager_logstash_formatter}
]}
]},
{extra_sinks, [
{shortener_access_lager_event, [
{handlers, [
{lager_file_backend, [
{file, "access_log.json"},
{level, info},
{formatter, lager_logstash_formatter}
]}
]},
{async_threshold, 20},
{async_threshold_window, 5}
]}
]}
]},
{shortener, [
{ip, "::"},
{port, 8080},
{authorizers, #{
jwt => #{
signee => capi,
keyset => #{
keycloak => {pem_file, "var/keys/keycloak/public.pem"},
capi => {pem_file, "var/keys/capi/private.pem"}
}
}
}}
]}
].

6
config/vm.args Normal file
View File

@ -0,0 +1,6 @@
-sname shortener
-setcookie shortener_cookie
+K true
+A 10

View File

@ -27,7 +27,26 @@
]}.
% Common project dependencies.
{deps, []}.
{deps, [
{cowboy, "1.0.4"},
{jose, "1.7.9"},
{lager, "3.2.4"},
{genlib,
{git, "https://github.com/rbkmoney/genlib.git",
{branch, "master"}
}
},
{lager_logstash_formatter,
{git, "git@github.com:rbkmoney/lager_logstash_formatter.git",
{branch, "master"}
}
},
{cowboy_cors,
{git, "https://github.com/danielwhite/cowboy_cors.git",
{branch, "master"}
}
}
]}.
{xref_checks, [
undefined_function_calls,

View File

@ -1,14 +1,44 @@
{"1.1.0",
[{<<"jesse">>,
[{<<"base64url">>,{pkg,<<"base64url">>,<<"0.0.1">>},1},
{<<"cowboy">>,{pkg,<<"cowboy">>,<<"1.0.4">>},0},
{<<"cowboy_cors">>,
{git,"https://github.com/danielwhite/cowboy_cors.git",
{ref,"392f5804b63fff2bd0fda67671d5b2fbe0badd37"}},
0},
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},1},
{<<"genlib">>,
{git,"https://github.com/rbkmoney/genlib.git",
{ref,"7fc1ca1a57dbe2b8b837951095e314c32afd6c9a"}},
0},
{<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},1},
{<<"jesse">>,
{git,"git@github.com:rbkmoney/jesse.git",
{ref,"39105922d1ce5834383d8e8aa877c60319b9834a"}},
0},
{<<"jose">>,{pkg,<<"jose">>,<<"1.7.9">>},0},
{<<"jsx">>,{pkg,<<"jsx">>,<<"2.8.2">>},0},
{<<"lager">>,{pkg,<<"lager">>,<<"3.2.4">>},0},
{<<"lager_logstash_formatter">>,
{git,"git@github.com:rbkmoney/lager_logstash_formatter.git",
{ref,"83a0f21c03dacbd876c7289435f369f573c749b1"}},
0},
{<<"parse_trans">>,
{git,"https://github.com/rbkmoney/parse_trans.git",
{ref,"5ee45f5bfa6c04329bea3281977b774f04c89f11"}},
0},
{<<"pooler">>,{pkg,<<"pooler">>,<<"1.5.0">>},0},
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.4.0">>},1},
{<<"rfc3339">>,{pkg,<<"rfc3339">>,<<"0.2.1">>},0}]}.
[
{pkg_hash,[
{<<"base64url">>, <<"36A90125F5948E3AFD7BE97662A1504B934DD5DAC78451CA6E9ABF85A10286BE">>},
{<<"cowboy">>, <<"A324A8DF9F2316C833A470D918AAF73AE894278B8AA6226CE7A9BF699388F878">>},
{<<"cowlib">>, <<"9D769A1D062C9C3AC753096F868CA121E2730B9A377DE23DEC0F7E08B1DF84EE">>},
{<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>},
{<<"jose">>, <<"9DC5A14AB62DB4E41677FCC97993752562FB57AD0B8BA062589682EDD3ACB91F">>},
{<<"jsx">>, <<"7ACC7D785B5ABE8A6E9ADBDE926A24E481F29956DD8B4DF49E3E4E7BCC92A018">>},
{<<"lager">>, <<"A6DEB74DAE7927F46BD13255268308EF03EB206EC784A94EAF7C1C0F3B811615">>},
{<<"pooler">>, <<"0FD4BE5D2976E6A2E9A1617623031758C26F200C1FCA89E4A3C542747BEC6371">>},
{<<"ranch">>, <<"10272F95DA79340FA7E8774BA7930B901713D272905D0012B06CA6D994F8826B">>},
{<<"rfc3339">>, <<"0F037D3385A724BC78C732CF83384B2CC7A643F9ADDBF3FDB38A4BC6A6CD0069">>}]}
].