mirror of
https://github.com/valitydev/bouncer.git
synced 2024-11-06 02:15:18 +00:00
Enforce unified formatting w/ erlfmt (#18)
* Bump to rbkmoney/build_utils@e131872
This commit is contained in:
parent
db9721bf71
commit
6a7ca8f2e9
8
Makefile
8
Makefile
@ -19,7 +19,7 @@ BASE_IMAGE_TAG := 688cee70c0eb6540709fe35b816c81a90dc542ea
|
|||||||
BUILD_IMAGE_TAG := 917afcdd0c0a07bf4155d597bbba72e962e1a34a
|
BUILD_IMAGE_TAG := 917afcdd0c0a07bf4155d597bbba72e962e1a34a
|
||||||
CALL_ANYWHERE := \
|
CALL_ANYWHERE := \
|
||||||
submodules \
|
submodules \
|
||||||
all compile xref lint dialyze cover release clean distclean
|
all compile xref lint format check_format dialyze cover release clean distclean
|
||||||
|
|
||||||
CALL_W_CONTAINER := $(CALL_ANYWHERE) test
|
CALL_W_CONTAINER := $(CALL_ANYWHERE) test
|
||||||
|
|
||||||
@ -45,6 +45,12 @@ xref:
|
|||||||
lint:
|
lint:
|
||||||
$(REBAR) lint
|
$(REBAR) lint
|
||||||
|
|
||||||
|
check_format:
|
||||||
|
$(REBAR) fmt -c
|
||||||
|
|
||||||
|
format:
|
||||||
|
$(REBAR) fmt -w
|
||||||
|
|
||||||
dialyze:
|
dialyze:
|
||||||
$(REBAR) dialyzer
|
$(REBAR) dialyzer
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 2c4c2289ad7919ef953603f70d5cc967419ec2dd
|
Subproject commit e1318727d4d0c3e48f5122bf3197158b6695f50e
|
100
rebar.config
100
rebar.config
@ -1,6 +1,5 @@
|
|||||||
%% Common project erlang options.
|
%% Common project erlang options.
|
||||||
{erl_opts, [
|
{erl_opts, [
|
||||||
|
|
||||||
% mandatory
|
% mandatory
|
||||||
debug_info,
|
debug_info,
|
||||||
warnings_as_errors,
|
warnings_as_errors,
|
||||||
@ -27,74 +26,38 @@
|
|||||||
|
|
||||||
%% Common project dependencies.
|
%% Common project dependencies.
|
||||||
{deps, [
|
{deps, [
|
||||||
|
|
||||||
{cowboy, "2.8.0"},
|
{cowboy, "2.8.0"},
|
||||||
{jsx, "3.0.0"},
|
{jsx, "3.0.0"},
|
||||||
{jesse, "1.5.5"},
|
{jesse, "1.5.5"},
|
||||||
{gun,
|
{gun, {git, "https://github.com/ninenines/gun.git", {branch, "master"}}},
|
||||||
{git, "https://github.com/ninenines/gun.git",
|
{genlib, {git, "https://github.com/rbkmoney/genlib.git", {branch, "master"}}},
|
||||||
{branch, "master"}
|
{thrift, {git, "https://github.com/rbkmoney/thrift_erlang.git", {branch, "master"}}},
|
||||||
}},
|
{woody, {git, "https://github.com/rbkmoney/woody_erlang.git", {branch, "master"}}},
|
||||||
{genlib,
|
|
||||||
{git, "https://github.com/rbkmoney/genlib.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{thrift,
|
|
||||||
{git, "https://github.com/rbkmoney/thrift_erlang.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{woody,
|
|
||||||
{git, "https://github.com/rbkmoney/woody_erlang.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{woody_user_identity,
|
{woody_user_identity,
|
||||||
{git, "https://github.com/rbkmoney/woody_erlang_user_identity.git",
|
{git, "https://github.com/rbkmoney/woody_erlang_user_identity.git", {branch, "master"}}},
|
||||||
{branch, "master"}
|
{bouncer_proto, {git, "git@github.com:rbkmoney/bouncer-proto.git", {branch, "master"}}},
|
||||||
}
|
|
||||||
},
|
|
||||||
{bouncer_proto,
|
|
||||||
{git, "git@github.com:rbkmoney/bouncer-proto.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{org_management_proto,
|
{org_management_proto,
|
||||||
{git, "git@github.com:rbkmoney/org-management-proto.git",
|
{git, "git@github.com:rbkmoney/org-management-proto.git", {branch, "master"}}},
|
||||||
{branch, "master"}
|
{scoper, {git, "https://github.com/rbkmoney/scoper.git", {branch, "master"}}},
|
||||||
}
|
{erl_health, {git, "https://github.com/rbkmoney/erlang-health.git", {branch, "master"}}},
|
||||||
},
|
|
||||||
{scoper,
|
|
||||||
{git, "https://github.com/rbkmoney/scoper.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{erl_health,
|
|
||||||
{git, "https://github.com/rbkmoney/erlang-health.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
% Production-only deps.
|
% Production-only deps.
|
||||||
% Defined here for the sake of rebar-locking.
|
% Defined here for the sake of rebar-locking.
|
||||||
{recon, "2.5.1"},
|
{recon, "2.5.1"},
|
||||||
{logger_logstash_formatter,
|
{logger_logstash_formatter,
|
||||||
{git, "https://github.com/rbkmoney/logger_logstash_formatter.git",
|
{git, "https://github.com/rbkmoney/logger_logstash_formatter.git", {branch, "master"}}},
|
||||||
{branch, "master"}
|
{how_are_you, {git, "https://github.com/rbkmoney/how_are_you.git", {branch, "master"}}}
|
||||||
}
|
|
||||||
},
|
|
||||||
{how_are_you,
|
|
||||||
{git, "https://github.com/rbkmoney/how_are_you.git",
|
|
||||||
{branch, "master"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
%% Helpful plugins.
|
%% Helpful plugins.
|
||||||
{plugins, [
|
{plugins, [
|
||||||
rebar3_lint
|
rebar3_lint,
|
||||||
|
{erlfmt, "0.10.0"}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
{erlfmt, [
|
||||||
|
{print_width, 100},
|
||||||
|
{files, ["{src,include,test}/*.{hrl,erl}", "rebar.config"]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
%% Linter config.
|
%% Linter config.
|
||||||
@ -104,10 +67,12 @@
|
|||||||
filter => "*.erl",
|
filter => "*.erl",
|
||||||
ruleset => erl_files,
|
ruleset => erl_files,
|
||||||
rules => [
|
rules => [
|
||||||
{elvis_style, invalid_dynamic_call, #{ignore => [
|
{elvis_style, invalid_dynamic_call, #{
|
||||||
% Uses thrift reflection through `struct_info/1`.
|
ignore => [
|
||||||
bouncer_thrift
|
% Uses thrift reflection through `struct_info/1`.
|
||||||
]}}
|
bouncer_thrift
|
||||||
|
]
|
||||||
|
}}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
#{
|
#{
|
||||||
@ -161,6 +126,7 @@
|
|||||||
deprecated_functions_calls,
|
deprecated_functions_calls,
|
||||||
deprecated_functions
|
deprecated_functions
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
% at will
|
% at will
|
||||||
% {xref_warnings, true}.
|
% {xref_warnings, true}.
|
||||||
|
|
||||||
@ -169,11 +135,15 @@
|
|||||||
|
|
||||||
%% Relx configuration
|
%% Relx configuration
|
||||||
{relx, [
|
{relx, [
|
||||||
{release, {bouncer , "0.1.0"}, [
|
{release, {bouncer, "0.1.0"}, [
|
||||||
{recon , load}, % tools for introspection
|
% tools for introspection
|
||||||
{runtime_tools, load}, % debugger
|
{recon, load},
|
||||||
{tools , load}, % profiler
|
% debugger
|
||||||
{logger_logstash_formatter, load}, % logger formatter
|
{runtime_tools, load},
|
||||||
|
% profiler
|
||||||
|
{tools, load},
|
||||||
|
% logger formatter
|
||||||
|
{logger_logstash_formatter, load},
|
||||||
how_are_you,
|
how_are_you,
|
||||||
bouncer
|
bouncer
|
||||||
]},
|
]},
|
||||||
@ -197,7 +167,6 @@
|
|||||||
]}.
|
]}.
|
||||||
|
|
||||||
{profiles, [
|
{profiles, [
|
||||||
|
|
||||||
{prod, [
|
{prod, [
|
||||||
{relx, [
|
{relx, [
|
||||||
{dev_mode, false},
|
{dev_mode, false},
|
||||||
@ -210,5 +179,4 @@
|
|||||||
{cover_enabled, true},
|
{cover_enabled, true},
|
||||||
{deps, []}
|
{deps, []}
|
||||||
]}
|
]}
|
||||||
|
|
||||||
]}.
|
]}.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
%% Application callbacks
|
%% Application callbacks
|
||||||
|
|
||||||
-behaviour(application).
|
-behaviour(application).
|
||||||
|
|
||||||
-export([start/2]).
|
-export([start/2]).
|
||||||
-export([prep_stop/1]).
|
-export([prep_stop/1]).
|
||||||
-export([stop/1]).
|
-export([stop/1]).
|
||||||
@ -10,18 +11,16 @@
|
|||||||
%% Supervisor callbacks
|
%% Supervisor callbacks
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
|
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec start(normal, any()) ->
|
-spec start(normal, any()) -> {ok, pid()} | {error, any()}.
|
||||||
{ok, pid()} | {error, any()}.
|
|
||||||
|
|
||||||
start(_StartType, _StartArgs) ->
|
start(_StartType, _StartArgs) ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
-spec prep_stop(State) ->
|
-spec prep_stop(State) -> State.
|
||||||
State.
|
|
||||||
prep_stop(State) ->
|
prep_stop(State) ->
|
||||||
% NOTE
|
% NOTE
|
||||||
% We have to do it in this magic `prep_stop/1` here because for some inexplicable reason the
|
% We have to do it in this magic `prep_stop/1` here because for some inexplicable reason the
|
||||||
@ -29,17 +28,13 @@ prep_stop(State) ->
|
|||||||
ok = bouncer_audit_log:stop(genlib_app:env(?MODULE, audit, #{})),
|
ok = bouncer_audit_log:stop(genlib_app:env(?MODULE, audit, #{})),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec stop(any()) ->
|
-spec stop(any()) -> ok.
|
||||||
ok.
|
|
||||||
|
|
||||||
stop(_State) ->
|
stop(_State) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec init([]) ->
|
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
|
||||||
{ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
|
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
AuditPulse = bouncer_audit_log:init(genlib_app:env(?MODULE, audit, #{})),
|
AuditPulse = bouncer_audit_log:init(genlib_app:env(?MODULE, audit, #{})),
|
||||||
ServiceOpts = genlib_app:env(?MODULE, services, #{}),
|
ServiceOpts = genlib_app:env(?MODULE, services, #{}),
|
||||||
@ -48,56 +43,46 @@ init([]) ->
|
|||||||
ChildSpec = woody_server:child_spec(
|
ChildSpec = woody_server:child_spec(
|
||||||
?MODULE,
|
?MODULE,
|
||||||
#{
|
#{
|
||||||
ip => get_ip_address(),
|
ip => get_ip_address(),
|
||||||
port => get_port(),
|
port => get_port(),
|
||||||
protocol_opts => get_protocol_opts(),
|
protocol_opts => get_protocol_opts(),
|
||||||
transport_opts => get_transport_opts(),
|
transport_opts => get_transport_opts(),
|
||||||
shutdown_timeout => get_shutdown_timeout(),
|
shutdown_timeout => get_shutdown_timeout(),
|
||||||
event_handler => EventHandlers,
|
event_handler => EventHandlers,
|
||||||
handlers =>
|
handlers =>
|
||||||
get_handler_specs(ServiceOpts, AuditPulse) ++ get_stub_handler_specs(ServiceOpts),
|
get_handler_specs(ServiceOpts, AuditPulse) ++ get_stub_handler_specs(ServiceOpts),
|
||||||
additional_routes => [erl_health_handle:get_route(Healthcheck)]
|
additional_routes => [erl_health_handle:get_route(Healthcheck)]
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
{ok, {
|
{ok,
|
||||||
#{strategy => one_for_all, intensity => 6, period => 30},
|
{
|
||||||
[ChildSpec]
|
#{strategy => one_for_all, intensity => 6, period => 30},
|
||||||
}}.
|
[ChildSpec]
|
||||||
|
}}.
|
||||||
-spec get_ip_address() ->
|
|
||||||
inet:ip_address().
|
|
||||||
|
|
||||||
|
-spec get_ip_address() -> inet:ip_address().
|
||||||
get_ip_address() ->
|
get_ip_address() ->
|
||||||
{ok, Address} = inet:parse_address(genlib_app:env(?MODULE, ip, "::")),
|
{ok, Address} = inet:parse_address(genlib_app:env(?MODULE, ip, "::")),
|
||||||
Address.
|
Address.
|
||||||
|
|
||||||
-spec get_port() ->
|
-spec get_port() -> inet:port_number().
|
||||||
inet:port_number().
|
|
||||||
|
|
||||||
get_port() ->
|
get_port() ->
|
||||||
genlib_app:env(?MODULE, port, 8022).
|
genlib_app:env(?MODULE, port, 8022).
|
||||||
|
|
||||||
-spec get_protocol_opts() ->
|
-spec get_protocol_opts() -> woody_server_thrift_http_handler:protocol_opts().
|
||||||
woody_server_thrift_http_handler:protocol_opts().
|
|
||||||
|
|
||||||
get_protocol_opts() ->
|
get_protocol_opts() ->
|
||||||
genlib_app:env(?MODULE, protocol_opts, #{}).
|
genlib_app:env(?MODULE, protocol_opts, #{}).
|
||||||
|
|
||||||
-spec get_transport_opts() ->
|
-spec get_transport_opts() -> woody_server_thrift_http_handler:transport_opts().
|
||||||
woody_server_thrift_http_handler:transport_opts().
|
|
||||||
|
|
||||||
get_transport_opts() ->
|
get_transport_opts() ->
|
||||||
genlib_app:env(?MODULE, transport_opts, #{}).
|
genlib_app:env(?MODULE, transport_opts, #{}).
|
||||||
|
|
||||||
-spec get_shutdown_timeout() ->
|
-spec get_shutdown_timeout() -> timeout().
|
||||||
timeout().
|
|
||||||
|
|
||||||
get_shutdown_timeout() ->
|
get_shutdown_timeout() ->
|
||||||
genlib_app:env(?MODULE, shutdown_timeout, 0).
|
genlib_app:env(?MODULE, shutdown_timeout, 0).
|
||||||
|
|
||||||
-spec get_handler_specs(map(), bouncer_arbiter_pulse:handlers()) ->
|
-spec get_handler_specs(map(), bouncer_arbiter_pulse:handlers()) ->
|
||||||
[woody:http_handler(woody:th_handler())].
|
[woody:http_handler(woody:th_handler())].
|
||||||
|
|
||||||
get_handler_specs(ServiceOpts, AuditPulse) ->
|
get_handler_specs(ServiceOpts, AuditPulse) ->
|
||||||
ArbiterService = maps:get(arbiter, ServiceOpts, #{}),
|
ArbiterService = maps:get(arbiter, ServiceOpts, #{}),
|
||||||
ArbiterPulse = maps:get(pulse, ArbiterService, []),
|
ArbiterPulse = maps:get(pulse, ArbiterService, []),
|
||||||
@ -122,12 +107,10 @@ get_stub_handler_specs(ServiceOpts) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec enable_health_logging(erl_health:check()) ->
|
-spec enable_health_logging(erl_health:check()) -> erl_health:check().
|
||||||
erl_health:check().
|
|
||||||
|
|
||||||
enable_health_logging(Check) ->
|
enable_health_logging(Check) ->
|
||||||
EvHandler = {erl_health_event_handler, []},
|
EvHandler = {erl_health_event_handler, []},
|
||||||
maps:map(
|
maps:map(
|
||||||
fun (_, Runner) -> #{runner => Runner, event_handler => EvHandler} end,
|
fun(_, Runner) -> #{runner => Runner, event_handler => EvHandler} end,
|
||||||
Check
|
Check
|
||||||
).
|
).
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
%% ```
|
%% ```
|
||||||
-type ruleset_id() :: iodata().
|
-type ruleset_id() :: iodata().
|
||||||
|
|
||||||
-type judgement() :: {resolution(), [assertion()]}.
|
-type judgement() :: {resolution(), [assertion()]}.
|
||||||
-type resolution() :: allowed | forbidden | {restricted, map()}.
|
-type resolution() :: allowed | forbidden | {restricted, map()}.
|
||||||
-type assertion() :: {_Code :: binary(), _Details :: #{binary() => _}}.
|
-type assertion() :: {_Code :: binary(), _Details :: #{binary() => _}}.
|
||||||
|
|
||||||
-export_type([judgement/0]).
|
-export_type([judgement/0]).
|
||||||
-export_type([resolution/0]).
|
-export_type([resolution/0]).
|
||||||
@ -26,12 +26,11 @@
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
-spec judge(ruleset_id(), bouncer_context:ctx()) ->
|
-spec judge(ruleset_id(), bouncer_context:ctx()) ->
|
||||||
{ok, judgement()} |
|
{ok, judgement()}
|
||||||
{error,
|
| {error,
|
||||||
ruleset_notfound |
|
ruleset_notfound
|
||||||
{ruleset_invalid, _Details} |
|
| {ruleset_invalid, _Details}
|
||||||
{unavailable | unknown, _Reason}
|
| {unavailable | unknown, _Reason}}.
|
||||||
}.
|
|
||||||
judge(RulesetID, Context) ->
|
judge(RulesetID, Context) ->
|
||||||
case mk_opa_client() of
|
case mk_opa_client() of
|
||||||
{ok, Client} ->
|
{ok, Client} ->
|
||||||
@ -48,8 +47,7 @@ judge(RulesetID, Context) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec infer_judgement(document()) ->
|
-spec infer_judgement(document()) -> {ok, judgement()} | {error, {ruleset_invalid, _Details}}.
|
||||||
{ok, judgement()} | {error, {ruleset_invalid, _Details}}.
|
|
||||||
infer_judgement(Document) ->
|
infer_judgement(Document) ->
|
||||||
case jesse:validate_with_schema(get_judgement_schema(), Document) of
|
case jesse:validate_with_schema(get_judgement_schema(), Document) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
@ -74,8 +72,7 @@ extract_assertions(Assertions) ->
|
|||||||
extract_assertion(Assertion = #{<<"code">> := Code}) ->
|
extract_assertion(Assertion = #{<<"code">> := Code}) ->
|
||||||
{Code, maps:without([<<"code">>], Assertion)}.
|
{Code, maps:without([<<"code">>], Assertion)}.
|
||||||
|
|
||||||
-spec get_judgement_schema() ->
|
-spec get_judgement_schema() -> jesse:schema().
|
||||||
jesse:schema().
|
|
||||||
get_judgement_schema() ->
|
get_judgement_schema() ->
|
||||||
% TODO
|
% TODO
|
||||||
% Worth declaring in a separate file? Should be helpful w/ CI-like activities.
|
% Worth declaring in a separate file? Should be helpful w/ CI-like activities.
|
||||||
@ -121,37 +118,36 @@ get_judgement_schema() ->
|
|||||||
|
|
||||||
-type endpoint() :: {inet:hostname() | inet:ip_address(), inet:port_number()}.
|
-type endpoint() :: {inet:hostname() | inet:ip_address(), inet:port_number()}.
|
||||||
-type client_opts() :: #{
|
-type client_opts() :: #{
|
||||||
endpoint := endpoint(),
|
endpoint := endpoint(),
|
||||||
transport => tcp | tls,
|
transport => tcp | tls,
|
||||||
tcp_opts => [gen_tcp:connect_option()],
|
tcp_opts => [gen_tcp:connect_option()],
|
||||||
tls_opts => [ssl:tls_client_option()],
|
tls_opts => [ssl:tls_client_option()],
|
||||||
connect_timeout => timeout(),
|
connect_timeout => timeout(),
|
||||||
domain_lookup_timeout => timeout(),
|
domain_lookup_timeout => timeout(),
|
||||||
request_timeout => timeout(),
|
request_timeout => timeout(),
|
||||||
http_opts => gun:http_opts(),
|
http_opts => gun:http_opts(),
|
||||||
http2_opts => gun:http2_opts(),
|
http2_opts => gun:http2_opts(),
|
||||||
% TODO
|
% TODO
|
||||||
% Pulse over gun event handler mechanic.
|
% Pulse over gun event handler mechanic.
|
||||||
event_handler => {module(), _State}
|
event_handler => {module(), _State}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-define(DEFAULT_CLIENT_OPTS, #{
|
-define(DEFAULT_CLIENT_OPTS, #{
|
||||||
domain_lookup_timeout => 1000,
|
domain_lookup_timeout => 1000,
|
||||||
connect_timeout => 1000,
|
connect_timeout => 1000,
|
||||||
request_timeout => 1000
|
request_timeout => 1000
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type client() :: {pid(), client_opts()}.
|
-type client() :: {pid(), client_opts()}.
|
||||||
-type document() ::
|
-type document() ::
|
||||||
null |
|
null
|
||||||
binary() |
|
| binary()
|
||||||
number() |
|
| number()
|
||||||
boolean() |
|
| boolean()
|
||||||
#{atom() | binary() => document()} |
|
| #{atom() | binary() => document()}
|
||||||
[document()].
|
| [document()].
|
||||||
|
|
||||||
-spec mk_opa_client() ->
|
-spec mk_opa_client() -> {ok, client()} | {error, {unavailable, _Reason}}.
|
||||||
{ok, client()} | {error, {unavailable, _Reason}}.
|
|
||||||
mk_opa_client() ->
|
mk_opa_client() ->
|
||||||
Opts = get_opa_client_opts(),
|
Opts = get_opa_client_opts(),
|
||||||
{Host, Port} = maps:get(endpoint, Opts),
|
{Host, Port} = maps:get(endpoint, Opts),
|
||||||
@ -186,11 +182,10 @@ mk_opa_client() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-spec request_opa_document(_ID :: iodata(), _Input :: document(), client()) ->
|
-spec request_opa_document(_ID :: iodata(), _Input :: document(), client()) ->
|
||||||
{ok, document()} |
|
{ok, document()}
|
||||||
{error,
|
| {error,
|
||||||
notfound |
|
notfound
|
||||||
{unknown, _Reason}
|
| {unknown, _Reason}}.
|
||||||
}.
|
|
||||||
request_opa_document(ID, Input, {Client, Opts}) ->
|
request_opa_document(ID, Input, {Client, Opts}) ->
|
||||||
Path = join_path(<<"/v1/data">>, ID),
|
Path = join_path(<<"/v1/data">>, ID),
|
||||||
% TODO
|
% TODO
|
||||||
@ -200,11 +195,12 @@ request_opa_document(ID, Input, {Client, Opts}) ->
|
|||||||
CType = <<"application/json; charset=utf-8">>,
|
CType = <<"application/json; charset=utf-8">>,
|
||||||
Headers = #{
|
Headers = #{
|
||||||
<<"content-type">> => CType,
|
<<"content-type">> => CType,
|
||||||
<<"accept">> => CType
|
<<"accept">> => CType
|
||||||
},
|
},
|
||||||
Timeout = maps:get(request_timeout, Opts),
|
Timeout = maps:get(request_timeout, Opts),
|
||||||
StreamRef = gun:post(Client, Path, Headers, Body),
|
StreamRef = gun:post(Client, Path, Headers, Body),
|
||||||
Deadline = erlang:monotonic_time(millisecond) + Timeout, % TODO think about implications
|
% TODO think about implications
|
||||||
|
Deadline = erlang:monotonic_time(millisecond) + Timeout,
|
||||||
case gun:await(Client, StreamRef, Timeout) of
|
case gun:await(Client, StreamRef, Timeout) of
|
||||||
{response, nofin, 200, _Headers} ->
|
{response, nofin, 200, _Headers} ->
|
||||||
TimeoutLeft = Deadline - erlang:monotonic_time(millisecond),
|
TimeoutLeft = Deadline - erlang:monotonic_time(millisecond),
|
||||||
@ -222,8 +218,7 @@ request_opa_document(ID, Input, {Client, Opts}) ->
|
|||||||
{error, {unknown, Reason}}
|
{error, {unknown, Reason}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec decode_document(binary()) ->
|
-spec decode_document(binary()) -> {ok, document()} | {error, notfound}.
|
||||||
{ok, document()} | {error, notfound}.
|
|
||||||
decode_document(Response) ->
|
decode_document(Response) ->
|
||||||
case jsx:decode(Response) of
|
case jsx:decode(Response) of
|
||||||
#{<<"result">> := Result} ->
|
#{<<"result">> := Result} ->
|
||||||
@ -232,8 +227,7 @@ decode_document(Response) ->
|
|||||||
{error, notfound}
|
{error, notfound}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_opa_client_opts() ->
|
-spec get_opa_client_opts() -> client_opts().
|
||||||
client_opts().
|
|
||||||
get_opa_client_opts() ->
|
get_opa_client_opts() ->
|
||||||
maps:merge(
|
maps:merge(
|
||||||
?DEFAULT_CLIENT_OPTS,
|
?DEFAULT_CLIENT_OPTS,
|
||||||
@ -249,7 +243,7 @@ normalize_path(P = <<$/, P1/binary>>) ->
|
|||||||
S1 = byte_size(P1),
|
S1 = byte_size(P1),
|
||||||
case S1 > 0 andalso binary:last(P1) of
|
case S1 > 0 andalso binary:last(P1) of
|
||||||
$/ -> binary:part(P, 0, S1);
|
$/ -> binary:part(P, 0, S1);
|
||||||
_ -> P
|
_ -> P
|
||||||
end;
|
end;
|
||||||
normalize_path(P) when is_binary(P) ->
|
normalize_path(P) when is_binary(P) ->
|
||||||
normalize_path(<<$/, P/binary>>);
|
normalize_path(<<$/, P/binary>>);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
%% Woody handler
|
%% Woody handler
|
||||||
|
|
||||||
-behaviour(woody_server_thrift_handler).
|
-behaviour(woody_server_thrift_handler).
|
||||||
|
|
||||||
-export([handle_function/4]).
|
-export([handle_function/4]).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -14,7 +15,7 @@
|
|||||||
}.
|
}.
|
||||||
|
|
||||||
-record(st, {
|
-record(st, {
|
||||||
pulse :: bouncer_arbiter_pulse:handlers(),
|
pulse :: bouncer_arbiter_pulse:handlers(),
|
||||||
pulse_metadata :: bouncer_arbiter_pulse:metadata()
|
pulse_metadata :: bouncer_arbiter_pulse:metadata()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
@ -27,10 +28,12 @@ handle_function(Fn, Args, WoodyCtx, Opts) ->
|
|||||||
|
|
||||||
do_handle_function('Judge', {RulesetID, ContextIn}, WoodyCtx, Opts) ->
|
do_handle_function('Judge', {RulesetID, ContextIn}, WoodyCtx, Opts) ->
|
||||||
St = #st{
|
St = #st{
|
||||||
pulse = maps:get(pulse, Opts, []),
|
pulse = maps:get(pulse, Opts, []),
|
||||||
pulse_metadata = #{woody_ctx => WoodyCtx}
|
pulse_metadata = #{woody_ctx => WoodyCtx}
|
||||||
},
|
},
|
||||||
try handle_judge(RulesetID, ContextIn, St) catch
|
try
|
||||||
|
handle_judge(RulesetID, ContextIn, St)
|
||||||
|
catch
|
||||||
throw:{woody, Class, Details} ->
|
throw:{woody, Class, Details} ->
|
||||||
woody_error:raise(Class, Details);
|
woody_error:raise(Class, Details);
|
||||||
C:R:S ->
|
C:R:S ->
|
||||||
@ -58,8 +61,7 @@ handle_judge(RulesetID, ContextIn, St0) ->
|
|||||||
handle_network_error(Reason, St2)
|
handle_network_error(Reason, St2)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec handle_network_error(_Reason, st()) ->
|
-spec handle_network_error(_Reason, st()) -> no_return().
|
||||||
no_return().
|
|
||||||
handle_network_error({unavailable, Reason} = Error, St) ->
|
handle_network_error({unavailable, Reason} = Error, St) ->
|
||||||
ok = handle_judgement_beat({failed, Error}, St),
|
ok = handle_judgement_beat({failed, Error}, St),
|
||||||
throw({woody, system, {external, resource_unavailable, genlib:format(Reason)}});
|
throw({woody, system, {external, resource_unavailable, genlib:format(Reason)}});
|
||||||
@ -69,16 +71,15 @@ handle_network_error({unknown, Reason} = Error, St) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-type fragment_id() :: binary().
|
-type fragment_id() :: binary().
|
||||||
-type fragment_metadata() :: #{atom() => _}.
|
-type fragment_metadata() :: #{atom() => _}.
|
||||||
|
|
||||||
-type thrift_judgement() :: bouncer_decisions_thrift:'Judgement'().
|
-type thrift_judgement() :: bouncer_decisions_thrift:'Judgement'().
|
||||||
-type thrift_context() :: bouncer_decisions_thrift:'Context'().
|
-type thrift_context() :: bouncer_decisions_thrift:'Context'().
|
||||||
-type thrift_fragment() :: bouncer_context_thrift:'ContextFragment'().
|
-type thrift_fragment() :: bouncer_context_thrift:'ContextFragment'().
|
||||||
-type thrift_fragment_type() :: bouncer_context_thrift:'ContextFragmentType'().
|
-type thrift_fragment_type() :: bouncer_context_thrift:'ContextFragmentType'().
|
||||||
|
|
||||||
-spec encode_judgement(bouncer_arbiter:judgement()) ->
|
-spec encode_judgement(bouncer_arbiter:judgement()) -> thrift_judgement().
|
||||||
thrift_judgement().
|
|
||||||
encode_judgement({Resolution, _Assertions}) ->
|
encode_judgement({Resolution, _Assertions}) ->
|
||||||
#bdcs_Judgement{
|
#bdcs_Judgement{
|
||||||
resolution = encode_resolution(Resolution)
|
resolution = encode_resolution(Resolution)
|
||||||
@ -97,15 +98,14 @@ encode_restrictions(Restrictions) ->
|
|||||||
{struct, _, StructDef} = bouncer_restriction_thrift:struct_info('Restrictions'),
|
{struct, _, StructDef} = bouncer_restriction_thrift:struct_info('Restrictions'),
|
||||||
bouncer_thrift:json_to_thrift_struct(StructDef, Restrictions, #brstn_Restrictions{}).
|
bouncer_thrift:json_to_thrift_struct(StructDef, Restrictions, #brstn_Restrictions{}).
|
||||||
|
|
||||||
-spec decode_context(thrift_context(), st()) ->
|
-spec decode_context(thrift_context(), st()) -> {bouncer_context:ctx(), st()}.
|
||||||
{bouncer_context:ctx(), st()}.
|
|
||||||
decode_context(#bdcs_Context{fragments = FragmentsIn}, St0) ->
|
decode_context(#bdcs_Context{fragments = FragmentsIn}, St0) ->
|
||||||
% 1. Decode each fragment.
|
% 1. Decode each fragment.
|
||||||
{Fragments, St1} = decode_fragments(FragmentsIn, St0),
|
{Fragments, St1} = decode_fragments(FragmentsIn, St0),
|
||||||
% 2. Merge each decoded context into an empty context. Accumulate conflicts associated with
|
% 2. Merge each decoded context into an empty context. Accumulate conflicts associated with
|
||||||
% corresponding fragment id.
|
% corresponding fragment id.
|
||||||
{Ctx, Conflicts} = maps:fold(
|
{Ctx, Conflicts} = maps:fold(
|
||||||
fun (ID, Ctx, {CtxAcc, DiscardAcc}) ->
|
fun(ID, Ctx, {CtxAcc, DiscardAcc}) ->
|
||||||
case bouncer_context:merge(CtxAcc, Ctx) of
|
case bouncer_context:merge(CtxAcc, Ctx) of
|
||||||
{CtxAcc1, undefined} ->
|
{CtxAcc1, undefined} ->
|
||||||
{CtxAcc1, DiscardAcc};
|
{CtxAcc1, DiscardAcc};
|
||||||
@ -135,14 +135,14 @@ decode_context(#bdcs_Context{fragments = FragmentsIn}, St0) ->
|
|||||||
{#{fragment_id() => bouncer_context:ctx()}, st()}.
|
{#{fragment_id() => bouncer_context:ctx()}, st()}.
|
||||||
decode_fragments(Fragments, St0) ->
|
decode_fragments(Fragments, St0) ->
|
||||||
{Ctxs, Errors, PulseMeta} = maps:fold(
|
{Ctxs, Errors, PulseMeta} = maps:fold(
|
||||||
fun (ID, Fragment, {CtxAcc, ErrorAcc, PulseMetaAcc}) ->
|
fun(ID, Fragment, {CtxAcc, ErrorAcc, PulseMetaAcc}) ->
|
||||||
Type = Fragment#bctx_ContextFragment.type,
|
Type = Fragment#bctx_ContextFragment.type,
|
||||||
Content = genlib:define(Fragment#bctx_ContextFragment.content, <<>>),
|
Content = genlib:define(Fragment#bctx_ContextFragment.content, <<>>),
|
||||||
case decode_fragment(Type, Content) of
|
case decode_fragment(Type, Content) of
|
||||||
{ok, Ctx, Meta} ->
|
{ok, Ctx, Meta} ->
|
||||||
PulseMeta = #{
|
PulseMeta = #{
|
||||||
type => Type,
|
type => Type,
|
||||||
context => Ctx,
|
context => Ctx,
|
||||||
metadata => Meta
|
metadata => Meta
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -177,12 +177,10 @@ decode_fragment(v1_thrift_binary, Content) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec append_pulse_metadata(bouncer_arbiter_pulse:metadata(), st()) ->
|
-spec append_pulse_metadata(bouncer_arbiter_pulse:metadata(), st()) -> st().
|
||||||
st().
|
|
||||||
append_pulse_metadata(Metadata, St = #st{pulse_metadata = MetadataWas}) ->
|
append_pulse_metadata(Metadata, St = #st{pulse_metadata = MetadataWas}) ->
|
||||||
St#st{pulse_metadata = maps:merge(MetadataWas, Metadata)}.
|
St#st{pulse_metadata = maps:merge(MetadataWas, Metadata)}.
|
||||||
|
|
||||||
-spec handle_judgement_beat(_Beat, st()) ->
|
-spec handle_judgement_beat(_Beat, st()) -> ok.
|
||||||
ok.
|
|
||||||
handle_judgement_beat(Beat, #st{pulse = Pulse, pulse_metadata = Metadata}) ->
|
handle_judgement_beat(Beat, #st{pulse = Pulse, pulse_metadata = Metadata}) ->
|
||||||
bouncer_arbiter_pulse:handle_beat({judgement, Beat}, Metadata, Pulse).
|
bouncer_arbiter_pulse:handle_beat({judgement, Beat}, Metadata, Pulse).
|
||||||
|
@ -2,22 +2,21 @@
|
|||||||
|
|
||||||
-type beat() ::
|
-type beat() ::
|
||||||
{judgement,
|
{judgement,
|
||||||
started |
|
started
|
||||||
{completed, bouncer_arbiter:judgement()} |
|
| {completed, bouncer_arbiter:judgement()}
|
||||||
{failed, _Reason}
|
| {failed, _Reason}}.
|
||||||
}.
|
|
||||||
|
|
||||||
-type metadata() :: #{
|
-type metadata() :: #{
|
||||||
ruleset => id(),
|
ruleset => id(),
|
||||||
context => bouncer_context:ctx(),
|
context => bouncer_context:ctx(),
|
||||||
fragments => #{id() => fragment()},
|
fragments => #{id() => fragment()},
|
||||||
woody_ctx => woody_context:ctx()
|
woody_ctx => woody_context:ctx()
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-type id() :: binary().
|
-type id() :: binary().
|
||||||
-type fragment() :: #{
|
-type fragment() :: #{
|
||||||
type => atom(),
|
type => atom(),
|
||||||
context => bouncer_context:ctx(),
|
context => bouncer_context:ctx(),
|
||||||
metadata => map()
|
metadata => map()
|
||||||
}.
|
}.
|
||||||
|
|
||||||
@ -30,18 +29,17 @@
|
|||||||
-type handler(St) :: {module(), St}.
|
-type handler(St) :: {module(), St}.
|
||||||
-type handlers() :: [handler()].
|
-type handlers() :: [handler()].
|
||||||
-type handlers(St) :: [handler(St)].
|
-type handlers(St) :: [handler(St)].
|
||||||
|
|
||||||
-export_type([handler/0]).
|
-export_type([handler/0]).
|
||||||
-export_type([handler/1]).
|
-export_type([handler/1]).
|
||||||
-export_type([handlers/0]).
|
-export_type([handlers/0]).
|
||||||
-export_type([handlers/1]).
|
-export_type([handlers/1]).
|
||||||
|
|
||||||
-callback handle_beat(beat(), metadata(), _Opts) ->
|
-callback handle_beat(beat(), metadata(), _Opts) -> ok.
|
||||||
ok.
|
|
||||||
|
|
||||||
-export([handle_beat/3]).
|
-export([handle_beat/3]).
|
||||||
|
|
||||||
-spec handle_beat(beat(), metadata(), handlers()) ->
|
-spec handle_beat(beat(), metadata(), handlers()) -> ok.
|
||||||
ok.
|
|
||||||
handle_beat(Beat, Metadata, [{Mod, Opts} | Rest]) ->
|
handle_beat(Beat, Metadata, [{Mod, Opts} | Rest]) ->
|
||||||
% NOTE
|
% NOTE
|
||||||
% Generally, we don't want some fault to propagate from event handler to the business logic
|
% Generally, we don't want some fault to propagate from event handler to the business logic
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
-export([stop/1]).
|
-export([stop/1]).
|
||||||
|
|
||||||
-behaviour(bouncer_arbiter_pulse).
|
-behaviour(bouncer_arbiter_pulse).
|
||||||
|
|
||||||
-export([handle_beat/3]).
|
-export([handle_beat/3]).
|
||||||
|
|
||||||
-define(DEFAULT_LOG_LEVEL, notice).
|
-define(DEFAULT_LOG_LEVEL, notice).
|
||||||
@ -54,8 +55,7 @@
|
|||||||
-type st() ::
|
-type st() ::
|
||||||
{log, logger:level()}.
|
{log, logger:level()}.
|
||||||
|
|
||||||
-spec init(opts()) ->
|
-spec init(opts()) -> bouncer_arbiter_pulse:handlers(st()).
|
||||||
bouncer_arbiter_pulse:handlers(st()).
|
|
||||||
init(Opts) ->
|
init(Opts) ->
|
||||||
_ = assert_strict_opts(?OPTS, Opts),
|
_ = assert_strict_opts(?OPTS, Opts),
|
||||||
init_log_handler(maps:get(log, Opts, #{})).
|
init_log_handler(maps:get(log, Opts, #{})).
|
||||||
@ -66,7 +66,7 @@ init_log_handler(LogOpts = #{}) ->
|
|||||||
BackendConfig = mk_logger_backend_config(maps:get(backend, LogOpts, #{})),
|
BackendConfig = mk_logger_backend_config(maps:get(backend, LogOpts, #{})),
|
||||||
HandlerConfig0 = maps:with([formatter], LogOpts),
|
HandlerConfig0 = maps:with([formatter], LogOpts),
|
||||||
HandlerConfig1 = HandlerConfig0#{
|
HandlerConfig1 = HandlerConfig0#{
|
||||||
config => BackendConfig,
|
config => BackendConfig,
|
||||||
% NOTE
|
% NOTE
|
||||||
% This two options together ensure that _only_ audit logs will flow through to the backend.
|
% This two options together ensure that _only_ audit logs will flow through to the backend.
|
||||||
filters => [{domain, {fun logger_filters:domain/2, {log, sub, ?LOG_DOMAIN}}}],
|
filters => [{domain, {fun logger_filters:domain/2, {log, sub, ?LOG_DOMAIN}}}],
|
||||||
@ -93,11 +93,7 @@ mk_logger_backend_config(BackendOpts) ->
|
|||||||
Type = validate_log_type(maps:get(type, BackendOpts, standard_io)),
|
Type = validate_log_type(maps:get(type, BackendOpts, standard_io)),
|
||||||
mk_logger_backend_config(Type, BackendOpts).
|
mk_logger_backend_config(Type, BackendOpts).
|
||||||
|
|
||||||
validate_log_type(Type) when
|
validate_log_type(Type) when Type == standard_io; Type == standard_error; Type == file ->
|
||||||
Type == standard_io;
|
|
||||||
Type == standard_error;
|
|
||||||
Type == file
|
|
||||||
->
|
|
||||||
Type;
|
Type;
|
||||||
validate_log_type(Type) ->
|
validate_log_type(Type) ->
|
||||||
erlang:error(badarg, [Type]).
|
erlang:error(badarg, [Type]).
|
||||||
@ -158,13 +154,11 @@ assert_strict_opts(Ks, Opts) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec stop(opts()) ->
|
-spec stop(opts()) -> ok.
|
||||||
ok.
|
|
||||||
stop(Opts = #{}) ->
|
stop(Opts = #{}) ->
|
||||||
stop_log_handler(maps:get(log, Opts, #{})).
|
stop_log_handler(maps:get(log, Opts, #{})).
|
||||||
|
|
||||||
-spec stop_log_handler(log_opts()) ->
|
-spec stop_log_handler(log_opts()) -> ok.
|
||||||
ok.
|
|
||||||
stop_log_handler(LogOpts = #{}) ->
|
stop_log_handler(LogOpts = #{}) ->
|
||||||
Level = maps:get(level, LogOpts, ?DEFAULT_LOG_LEVEL),
|
Level = maps:get(level, LogOpts, ?DEFAULT_LOG_LEVEL),
|
||||||
ok = log(Level, "audit log stopped", #{}),
|
ok = log(Level, "audit log stopped", #{}),
|
||||||
@ -175,11 +169,10 @@ stop_log_handler(disabled) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-type beat() :: bouncer_arbiter_pulse:beat().
|
-type beat() :: bouncer_arbiter_pulse:beat().
|
||||||
-type metadata() :: bouncer_arbiter_pulse:metadata().
|
-type metadata() :: bouncer_arbiter_pulse:metadata().
|
||||||
|
|
||||||
-spec handle_beat(beat(), metadata(), st()) ->
|
-spec handle_beat(beat(), metadata(), st()) -> ok.
|
||||||
ok.
|
|
||||||
handle_beat(Beat, Metadata, {log, Level}) ->
|
handle_beat(Beat, Metadata, {log, Level}) ->
|
||||||
log(
|
log(
|
||||||
get_severity(Beat, Level),
|
get_severity(Beat, Level),
|
||||||
@ -200,34 +193,35 @@ log(Severity, Message, Metadata) ->
|
|||||||
ok.
|
ok.
|
||||||
|
|
||||||
get_severity({judgement, started}, _Level) -> debug;
|
get_severity({judgement, started}, _Level) -> debug;
|
||||||
get_severity(_, Level) -> Level.
|
get_severity(_, Level) -> Level.
|
||||||
|
|
||||||
get_message({judgement, started}) -> <<"judgement started">>;
|
get_message({judgement, started}) -> <<"judgement started">>;
|
||||||
get_message({judgement, {completed, _}}) -> <<"judgement completed">>;
|
get_message({judgement, {completed, _}}) -> <<"judgement completed">>;
|
||||||
get_message({judgement, {failed, _}}) -> <<"judgement failed">>.
|
get_message({judgement, {failed, _}}) -> <<"judgement failed">>.
|
||||||
|
|
||||||
get_beat_metadata({judgement, Event}) ->
|
get_beat_metadata({judgement, Event}) ->
|
||||||
#{
|
#{
|
||||||
judgement => case Event of
|
judgement =>
|
||||||
started ->
|
case Event of
|
||||||
#{
|
started ->
|
||||||
event => started
|
#{
|
||||||
};
|
event => started
|
||||||
{completed, {Resolution, Assertions}} ->
|
};
|
||||||
encode_restrictions(Resolution, #{
|
{completed, {Resolution, Assertions}} ->
|
||||||
event => completed,
|
encode_restrictions(Resolution, #{
|
||||||
resolution => encode_resolution(Resolution),
|
event => completed,
|
||||||
assertions => lists:map(fun encode_assertion/1, Assertions)
|
resolution => encode_resolution(Resolution),
|
||||||
});
|
assertions => lists:map(fun encode_assertion/1, Assertions)
|
||||||
{failed, Error} ->
|
});
|
||||||
#{
|
{failed, Error} ->
|
||||||
event => failed,
|
#{
|
||||||
error => encode_error(Error)
|
event => failed,
|
||||||
}
|
error => encode_error(Error)
|
||||||
end
|
}
|
||||||
|
end
|
||||||
}.
|
}.
|
||||||
|
|
||||||
encode_resolution(allowed) -> <<"allowed">>;
|
encode_resolution(allowed) -> <<"allowed">>;
|
||||||
encode_resolution(forbidden) -> <<"forbidden">>;
|
encode_resolution(forbidden) -> <<"forbidden">>;
|
||||||
encode_resolution({restricted, _Restrictions}) -> <<"restricted">>.
|
encode_resolution({restricted, _Restrictions}) -> <<"restricted">>.
|
||||||
|
|
||||||
@ -257,7 +251,7 @@ extract_metadata(Metadata, Acc) ->
|
|||||||
extract_opt_meta(K, Metadata, EncodeFun, Acc) ->
|
extract_opt_meta(K, Metadata, EncodeFun, Acc) ->
|
||||||
case maps:find(K, Metadata) of
|
case maps:find(K, Metadata) of
|
||||||
{ok, V} -> Acc#{K => EncodeFun(V)};
|
{ok, V} -> Acc#{K => EncodeFun(V)};
|
||||||
error -> Acc
|
error -> Acc
|
||||||
end.
|
end.
|
||||||
|
|
||||||
encode_id(ID) when is_binary(ID) ->
|
encode_id(ID) when is_binary(ID) ->
|
||||||
|
@ -23,17 +23,14 @@
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec empty() ->
|
-spec empty() -> ctx().
|
||||||
ctx().
|
|
||||||
empty() ->
|
empty() ->
|
||||||
#{}.
|
#{}.
|
||||||
|
|
||||||
-spec merge(ctx(), ctx()) ->
|
-spec merge(ctx(), ctx()) -> {_Merged :: ctx(), _Conflicting :: ctx() | undefined}.
|
||||||
{_Merged :: ctx(), _Conflicting :: ctx() | undefined}.
|
|
||||||
|
|
||||||
merge(Ctx1, Ctx2) ->
|
merge(Ctx1, Ctx2) ->
|
||||||
maps:fold(
|
maps:fold(
|
||||||
fun (K, V2, {CtxAcc, ConflictAcc}) ->
|
fun(K, V2, {CtxAcc, ConflictAcc}) ->
|
||||||
case maps:find(K, CtxAcc) of
|
case maps:find(K, CtxAcc) of
|
||||||
{ok, V1} ->
|
{ok, V1} ->
|
||||||
{VM, Conflict} = merge_values(V1, V2),
|
{VM, Conflict} = merge_values(V1, V2),
|
||||||
@ -54,10 +51,11 @@ merge_values(V1, V2) ->
|
|||||||
case ordsets:is_set(V1) and ordsets:is_set(V2) of
|
case ordsets:is_set(V1) and ordsets:is_set(V2) of
|
||||||
true ->
|
true ->
|
||||||
Intersection = ordsets:intersection(V1, V2),
|
Intersection = ordsets:intersection(V1, V2),
|
||||||
MaybeConflict = case ordsets:size(Intersection) of
|
MaybeConflict =
|
||||||
0 -> undefined;
|
case ordsets:size(Intersection) of
|
||||||
_ -> Intersection
|
0 -> undefined;
|
||||||
end,
|
_ -> Intersection
|
||||||
|
end,
|
||||||
{ordsets:union(V1, V2), MaybeConflict};
|
{ordsets:union(V1, V2), MaybeConflict};
|
||||||
false when V1 =:= V2 ->
|
false when V1 =:= V2 ->
|
||||||
{V2, undefined};
|
{V2, undefined};
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
-type metadata() :: #{
|
-type metadata() :: #{
|
||||||
version := #{
|
version := #{
|
||||||
current := vsn(),
|
current := vsn(),
|
||||||
original := vsn(),
|
original := vsn(),
|
||||||
latest := vsn()
|
latest := vsn()
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
@ -19,13 +19,13 @@
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
-define(THRIFT_TYPE,
|
-define(THRIFT_TYPE,
|
||||||
{struct, struct, {bouncer_context_v1_thrift, 'ContextFragment'}}).
|
{struct, struct, {bouncer_context_v1_thrift, 'ContextFragment'}}
|
||||||
|
).
|
||||||
|
|
||||||
-type thrift_ctx_fragment() :: bouncer_context_v1_thrift:'ContextFragment'().
|
-type thrift_ctx_fragment() :: bouncer_context_v1_thrift:'ContextFragment'().
|
||||||
|
|
||||||
-spec decode(format(), _Content :: binary()) ->
|
-spec decode(format(), _Content :: binary()) ->
|
||||||
{ok, bouncer_context:ctx(), metadata()} | {error, _Reason}.
|
{ok, bouncer_context:ctx(), metadata()} | {error, _Reason}.
|
||||||
|
|
||||||
decode(thrift, Content) ->
|
decode(thrift, Content) ->
|
||||||
Codec = thrift_strict_binary_codec:new(Content),
|
Codec = thrift_strict_binary_codec:new(Content),
|
||||||
case thrift_strict_binary_codec:read(Codec, ?THRIFT_TYPE) of
|
case thrift_strict_binary_codec:read(Codec, ?THRIFT_TYPE) of
|
||||||
@ -40,15 +40,14 @@ decode(thrift, Content) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec from_thrift(thrift_ctx_fragment()) ->
|
-spec from_thrift(thrift_ctx_fragment()) -> {ok, bouncer_context:ctx(), metadata()}.
|
||||||
{ok, bouncer_context:ctx(), metadata()}.
|
|
||||||
from_thrift(#bctx_v1_ContextFragment{} = Ctx0) ->
|
from_thrift(#bctx_v1_ContextFragment{} = Ctx0) ->
|
||||||
Ctx1 = try_upgrade(Ctx0),
|
Ctx1 = try_upgrade(Ctx0),
|
||||||
Metadata = #{
|
Metadata = #{
|
||||||
version => #{
|
version => #{
|
||||||
current => Ctx1#bctx_v1_ContextFragment.vsn,
|
current => Ctx1#bctx_v1_ContextFragment.vsn,
|
||||||
original => Ctx0#bctx_v1_ContextFragment.vsn,
|
original => Ctx0#bctx_v1_ContextFragment.vsn,
|
||||||
latest => ?BCTX_V1_HEAD
|
latest => ?BCTX_V1_HEAD
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ok, from_thrift_context(Ctx1), Metadata}.
|
{ok, from_thrift_context(Ctx1), Metadata}.
|
||||||
@ -60,15 +59,13 @@ from_thrift_context(Ctx) ->
|
|||||||
% This 3 refers to the first data field in a ContextFragment, after version field.
|
% This 3 refers to the first data field in a ContextFragment, after version field.
|
||||||
bouncer_thrift:from_thrift_struct(StructDef, Ctx, 3, #{}).
|
bouncer_thrift:from_thrift_struct(StructDef, Ctx, 3, #{}).
|
||||||
|
|
||||||
-spec try_upgrade(thrift_ctx_fragment()) ->
|
-spec try_upgrade(thrift_ctx_fragment()) -> thrift_ctx_fragment().
|
||||||
thrift_ctx_fragment().
|
|
||||||
try_upgrade(#bctx_v1_ContextFragment{vsn = ?BCTX_V1_HEAD} = Ctx) ->
|
try_upgrade(#bctx_v1_ContextFragment{vsn = ?BCTX_V1_HEAD} = Ctx) ->
|
||||||
Ctx.
|
Ctx.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec encode(format(), bouncer_context:ctx()) ->
|
-spec encode(format(), bouncer_context:ctx()) -> {ok, _Content} | {error, _}.
|
||||||
{ok, _Content} | {error, _}.
|
|
||||||
encode(thrift, Context) ->
|
encode(thrift, Context) ->
|
||||||
Codec = thrift_strict_binary_codec:new(),
|
Codec = thrift_strict_binary_codec:new(),
|
||||||
CtxThrift = to_thrift(Context),
|
CtxThrift = to_thrift(Context),
|
||||||
@ -79,8 +76,7 @@ encode(thrift, Context) ->
|
|||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec to_thrift(bouncer_context:ctx()) ->
|
-spec to_thrift(bouncer_context:ctx()) -> thrift_ctx_fragment() | no_return().
|
||||||
thrift_ctx_fragment() | no_return().
|
|
||||||
to_thrift(Context) ->
|
to_thrift(Context) ->
|
||||||
{struct, _, StructDef} = bouncer_context_v1_thrift:struct_info('ContextFragment'),
|
{struct, _, StructDef} = bouncer_context_v1_thrift:struct_info('ContextFragment'),
|
||||||
bouncer_thrift:to_thrift_struct(StructDef, Context, #bctx_v1_ContextFragment{}).
|
bouncer_thrift:to_thrift_struct(StructDef, Context, #bctx_v1_ContextFragment{}).
|
||||||
|
@ -12,12 +12,18 @@
|
|||||||
|
|
||||||
-type type_ref() :: {module(), atom()}.
|
-type type_ref() :: {module(), atom()}.
|
||||||
-type field_type() ::
|
-type field_type() ::
|
||||||
bool | byte | i16 | i32 | i64 | string | double |
|
bool
|
||||||
{enum, type_ref()} |
|
| byte
|
||||||
{struct, struct_flavour(), type_ref()} |
|
| i16
|
||||||
{list, field_type()} |
|
| i32
|
||||||
{set, field_type()} |
|
| i64
|
||||||
{map, field_type(), field_type()}.
|
| string
|
||||||
|
| double
|
||||||
|
| {enum, type_ref()}
|
||||||
|
| {struct, struct_flavour(), type_ref()}
|
||||||
|
| {list, field_type()}
|
||||||
|
| {set, field_type()}
|
||||||
|
| {map, field_type(), field_type()}.
|
||||||
|
|
||||||
-type struct_field_info() ::
|
-type struct_field_info() ::
|
||||||
{field_num(), field_req(), field_type(), field_name(), any()}.
|
{field_num(), field_req(), field_type(), field_name(), any()}.
|
||||||
|
@ -27,9 +27,7 @@
|
|||||||
-define(OPA_ENDPOINT, {?OPA_HOST, 8181}).
|
-define(OPA_ENDPOINT, {?OPA_HOST, 8181}).
|
||||||
-define(API_RULESET_ID, "service/authz/api").
|
-define(API_RULESET_ID, "service/authz/api").
|
||||||
|
|
||||||
-spec all() ->
|
-spec all() -> [atom()].
|
||||||
[atom()].
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[
|
[
|
||||||
invalid_config_fails_start,
|
invalid_config_fails_start,
|
||||||
@ -43,31 +41,24 @@ all() ->
|
|||||||
% write_queue_overload_fails_request
|
% write_queue_overload_fails_request
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
init_per_suite(C) ->
|
init_per_suite(C) ->
|
||||||
Apps =
|
Apps =
|
||||||
genlib_app:start_application(woody) ++
|
genlib_app:start_application(woody) ++
|
||||||
genlib_app:start_application_with(scoper, [
|
genlib_app:start_application_with(scoper, [
|
||||||
{storage, scoper_storage_logger}
|
{storage, scoper_storage_logger}
|
||||||
]),
|
]),
|
||||||
[{suite_apps, Apps} | C].
|
[{suite_apps, Apps} | C].
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
-spec end_per_suite(config()) -> ok.
|
||||||
ok.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
genlib_app:stop_unload_applications(?CONFIG(suite_apps, C)).
|
genlib_app:stop_unload_applications(?CONFIG(suite_apps, C)).
|
||||||
|
|
||||||
-spec init_per_testcase(testcase_name(), config()) ->
|
-spec init_per_testcase(testcase_name(), config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
[{testcase, Name} | C].
|
[{testcase, Name} | C].
|
||||||
|
|
||||||
-spec end_per_testcase(testcase_name(), config()) ->
|
-spec end_per_testcase(testcase_name(), config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
end_per_testcase(_Name, _C) ->
|
end_per_testcase(_Name, _C) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -83,48 +74,71 @@ end_per_testcase(_Name, _C) ->
|
|||||||
invalid_config_fails_start(C) ->
|
invalid_config_fails_start(C) ->
|
||||||
?assertError(
|
?assertError(
|
||||||
{bouncer, {{{badkey, file}, _Stacktrace}, _}},
|
{bouncer, {{{badkey, file}, _Stacktrace}, _}},
|
||||||
start_stop_bouncer([
|
start_stop_bouncer(
|
||||||
{audit, #{log => #{
|
[
|
||||||
backend => #{
|
{audit, #{
|
||||||
% NOTE
|
log => #{
|
||||||
% Missing target filename here.
|
backend => #{
|
||||||
type => file
|
% NOTE
|
||||||
}
|
% Missing target filename here.
|
||||||
}}}
|
type => file
|
||||||
], C)
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
)
|
||||||
),
|
),
|
||||||
?assertError(
|
?assertError(
|
||||||
{bouncer, {{badarg, _Stacktrace}, _}},
|
{bouncer, {{badarg, _Stacktrace}, _}},
|
||||||
start_stop_bouncer([
|
start_stop_bouncer(
|
||||||
{audit, #{log => #{
|
[
|
||||||
level => blarg
|
{audit, #{
|
||||||
}}}
|
log => #{
|
||||||
], C)
|
level => blarg
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
unrecognized_config_fails_start(C) ->
|
unrecognized_config_fails_start(C) ->
|
||||||
?assertError(
|
?assertError(
|
||||||
{bouncer, {{{unrecognized_opts, #{blarg := _}}, _Stacktrace}, _}},
|
{bouncer, {{{unrecognized_opts, #{blarg := _}}, _Stacktrace}, _}},
|
||||||
start_stop_bouncer([
|
start_stop_bouncer(
|
||||||
{audit, #{blarg => blorg}}
|
[
|
||||||
], C)
|
{audit, #{blarg => blorg}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
)
|
||||||
),
|
),
|
||||||
?assertError(
|
?assertError(
|
||||||
{bouncer, {{{unrecognized_opts, #{blarg := _}}, _Stacktrace}, _}},
|
{bouncer, {{{unrecognized_opts, #{blarg := _}}, _Stacktrace}, _}},
|
||||||
start_stop_bouncer([
|
start_stop_bouncer(
|
||||||
{audit, #{log => #{
|
[
|
||||||
blarg => blorg
|
{audit, #{
|
||||||
}}}
|
log => #{
|
||||||
], C)
|
blarg => blorg
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
)
|
||||||
),
|
),
|
||||||
?assertError(
|
?assertError(
|
||||||
{bouncer, {{{unrecognized_opts, #{hello := _}}, _Stacktrace}, _}},
|
{bouncer, {{{unrecognized_opts, #{hello := _}}, _Stacktrace}, _}},
|
||||||
start_stop_bouncer([
|
start_stop_bouncer(
|
||||||
{audit, #{log => #{
|
[
|
||||||
level => notice,
|
{audit, #{
|
||||||
hello => <<"mike">>
|
log => #{
|
||||||
}}}
|
level => notice,
|
||||||
], C)
|
hello => <<"mike">>
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
start_stop_bouncer(Env, C) ->
|
start_stop_bouncer(Env, C) ->
|
||||||
@ -135,11 +149,16 @@ start_stop_bouncer(Env, C) ->
|
|||||||
write_error_fails_request(C) ->
|
write_error_fails_request(C) ->
|
||||||
Dirname = mk_temp_dir(?CONFIG(testcase, C)),
|
Dirname = mk_temp_dir(?CONFIG(testcase, C)),
|
||||||
Filename = filename:join(Dirname, "audit.log"),
|
Filename = filename:join(Dirname, "audit.log"),
|
||||||
C1 = start_bouncer([{audit, #{
|
C1 = start_bouncer(
|
||||||
log => #{
|
[
|
||||||
backend => #{type => file, file => Filename}
|
{audit, #{
|
||||||
}
|
log => #{
|
||||||
}}], C),
|
backend => #{type => file, file => Filename}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
),
|
||||||
Client = mk_client(C1),
|
Client = mk_client(C1),
|
||||||
try
|
try
|
||||||
ok = file:delete(Filename),
|
ok = file:delete(Filename),
|
||||||
@ -158,24 +177,30 @@ write_queue_overload_fails_request(C) ->
|
|||||||
Concurrency = QLen * 10,
|
Concurrency = QLen * 10,
|
||||||
Dirname = mk_temp_dir(?CONFIG(testcase, C)),
|
Dirname = mk_temp_dir(?CONFIG(testcase, C)),
|
||||||
Filename = filename:join(Dirname, "audit.log"),
|
Filename = filename:join(Dirname, "audit.log"),
|
||||||
C1 = start_bouncer([{audit, #{
|
C1 = start_bouncer(
|
||||||
log => #{
|
[
|
||||||
backend => #{type => file, file => Filename, flush_qlen => QLen},
|
{audit, #{
|
||||||
formatter => {logger_logstash_formatter, #{single_line => true}}
|
log => #{
|
||||||
}
|
backend => #{type => file, file => Filename, flush_qlen => QLen},
|
||||||
}}], C),
|
formatter => {logger_logstash_formatter, #{single_line => true}}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
),
|
||||||
Client = mk_client(C1),
|
Client = mk_client(C1),
|
||||||
Results = genlib_pmap:safemap(
|
Results = genlib_pmap:safemap(
|
||||||
fun (_) ->
|
fun(_) ->
|
||||||
call_judge(?API_RULESET_ID, ?CONTEXT(#{}), Client)
|
call_judge(?API_RULESET_ID, ?CONTEXT(#{}), Client)
|
||||||
end,
|
end,
|
||||||
lists:seq(1, Concurrency)
|
lists:seq(1, Concurrency)
|
||||||
),
|
),
|
||||||
_ = stop_bouncer(C1),
|
_ = stop_bouncer(C1),
|
||||||
try
|
try
|
||||||
{Succeeded, _Failed} = lists:partition(fun ({R, _}) -> R == ok end, Results),
|
{Succeeded, _Failed} = lists:partition(fun({R, _}) -> R == ok end, Results),
|
||||||
{ok, LogfileContents} = file:read_file(Filename),
|
{ok, LogfileContents} = file:read_file(Filename),
|
||||||
NumLogEvents = binary:matches(LogfileContents, <<"\"type\":\"audit\"">>), % TODO kinda hacky
|
% TODO kinda hacky
|
||||||
|
NumLogEvents = binary:matches(LogfileContents, <<"\"type\":\"audit\"">>),
|
||||||
?assertEqual(length(Succeeded), length(NumLogEvents))
|
?assertEqual(length(Succeeded), length(NumLogEvents))
|
||||||
after
|
after
|
||||||
rm_temp_dir(Dirname)
|
rm_temp_dir(Dirname)
|
||||||
@ -229,21 +254,24 @@ start_bouncer(Env, C) ->
|
|||||||
IP = "127.0.0.1",
|
IP = "127.0.0.1",
|
||||||
Port = 8022,
|
Port = 8022,
|
||||||
ArbiterPath = <<"/v1/arbiter">>,
|
ArbiterPath = <<"/v1/arbiter">>,
|
||||||
Apps = start_application_with(bouncer, [
|
Apps = start_application_with(
|
||||||
{ip, IP},
|
bouncer,
|
||||||
{port, Port},
|
[
|
||||||
{services, #{
|
{ip, IP},
|
||||||
arbiter => #{path => ArbiterPath}
|
{port, Port},
|
||||||
}},
|
{services, #{
|
||||||
{transport_opts, #{
|
arbiter => #{path => ArbiterPath}
|
||||||
max_connections => 1000,
|
}},
|
||||||
num_acceptors => 4
|
{transport_opts, #{
|
||||||
}},
|
max_connections => 1000,
|
||||||
{opa, #{
|
num_acceptors => 4
|
||||||
endpoint => ?OPA_ENDPOINT,
|
}},
|
||||||
transport => tcp
|
{opa, #{
|
||||||
}}
|
endpoint => ?OPA_ENDPOINT,
|
||||||
] ++ Env),
|
transport => tcp
|
||||||
|
}}
|
||||||
|
] ++ Env
|
||||||
|
),
|
||||||
Services = #{
|
Services = #{
|
||||||
arbiter => mk_url(IP, Port, ArbiterPath)
|
arbiter => mk_url(IP, Port, ArbiterPath)
|
||||||
},
|
},
|
||||||
@ -253,8 +281,11 @@ mk_url(IP, Port, Path) ->
|
|||||||
iolist_to_binary(["http://", IP, ":", genlib:to_binary(Port), Path]).
|
iolist_to_binary(["http://", IP, ":", genlib:to_binary(Port), Path]).
|
||||||
|
|
||||||
stop_bouncer(C) ->
|
stop_bouncer(C) ->
|
||||||
ct_helper:with_config(testcase_apps, C,
|
ct_helper:with_config(
|
||||||
fun (Apps) -> genlib_app:stop_unload_applications(Apps) end).
|
testcase_apps,
|
||||||
|
C,
|
||||||
|
fun(Apps) -> genlib_app:stop_unload_applications(Apps) end
|
||||||
|
).
|
||||||
|
|
||||||
start_application_with(App, Env) ->
|
start_application_with(App, Env) ->
|
||||||
_ = application:load(App),
|
_ = application:load(App),
|
||||||
|
@ -28,16 +28,13 @@
|
|||||||
-define(OPA_ENDPOINT, {?OPA_HOST, 8181}).
|
-define(OPA_ENDPOINT, {?OPA_HOST, 8181}).
|
||||||
-define(API_RULESET_ID, "service/authz/api").
|
-define(API_RULESET_ID, "service/authz/api").
|
||||||
|
|
||||||
-spec all() ->
|
-spec all() -> [atom()].
|
||||||
[atom()].
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[
|
[
|
||||||
{group, general}
|
{group, general}
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec groups() ->
|
-spec groups() -> [{group_name(), list(), [test_case_name()]}].
|
||||||
[{group_name(), list(), [test_case_name()]}].
|
|
||||||
groups() ->
|
groups() ->
|
||||||
[
|
[
|
||||||
{general, [parallel], [
|
{general, [parallel], [
|
||||||
@ -45,27 +42,21 @@ groups() ->
|
|||||||
]}
|
]}
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
init_per_suite(C) ->
|
init_per_suite(C) ->
|
||||||
Apps =
|
Apps =
|
||||||
genlib_app:start_application(woody) ++
|
genlib_app:start_application(woody) ++
|
||||||
genlib_app:start_application_with(scoper, [
|
genlib_app:start_application_with(scoper, [
|
||||||
{storage, scoper_storage_logger}
|
{storage, scoper_storage_logger}
|
||||||
]),
|
]),
|
||||||
[{suite_apps, Apps} | C].
|
[{suite_apps, Apps} | C].
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
-spec end_per_suite(config()) -> ok.
|
||||||
ok.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
genlib_app:stop_unload_applications(?CONFIG(suite_apps, C)).
|
genlib_app:stop_unload_applications(?CONFIG(suite_apps, C)).
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) -> config().
|
||||||
config().
|
init_per_group(Name, C) when Name == general ->
|
||||||
init_per_group(Name, C) when
|
|
||||||
Name == general
|
|
||||||
->
|
|
||||||
start_bouncer([], C);
|
start_bouncer([], C);
|
||||||
init_per_group(_Name, C) ->
|
init_per_group(_Name, C) ->
|
||||||
C.
|
C.
|
||||||
@ -74,15 +65,18 @@ start_bouncer(Env, C) ->
|
|||||||
IP = "127.0.0.1",
|
IP = "127.0.0.1",
|
||||||
Port = 8022,
|
Port = 8022,
|
||||||
OrgmgmtPath = <<"/v1/org_management_stub">>,
|
OrgmgmtPath = <<"/v1/org_management_stub">>,
|
||||||
Apps = genlib_app:start_application_with(bouncer, [
|
Apps = genlib_app:start_application_with(
|
||||||
{ip, IP},
|
bouncer,
|
||||||
{port, Port},
|
[
|
||||||
{services, #{
|
{ip, IP},
|
||||||
org_management => #{
|
{port, Port},
|
||||||
path => OrgmgmtPath
|
{services, #{
|
||||||
}
|
org_management => #{
|
||||||
}}
|
path => OrgmgmtPath
|
||||||
] ++ Env),
|
}
|
||||||
|
}}
|
||||||
|
] ++ Env
|
||||||
|
),
|
||||||
Services = #{
|
Services = #{
|
||||||
org_management => mk_url(IP, Port, OrgmgmtPath)
|
org_management => mk_url(IP, Port, OrgmgmtPath)
|
||||||
},
|
},
|
||||||
@ -91,24 +85,22 @@ start_bouncer(Env, C) ->
|
|||||||
mk_url(IP, Port, Path) ->
|
mk_url(IP, Port, Path) ->
|
||||||
iolist_to_binary(["http://", IP, ":", genlib:to_binary(Port), Path]).
|
iolist_to_binary(["http://", IP, ":", genlib:to_binary(Port), Path]).
|
||||||
|
|
||||||
-spec end_per_group(group_name(), config()) ->
|
-spec end_per_group(group_name(), config()) -> _.
|
||||||
_.
|
|
||||||
end_per_group(_Name, C) ->
|
end_per_group(_Name, C) ->
|
||||||
stop_bouncer(C).
|
stop_bouncer(C).
|
||||||
|
|
||||||
stop_bouncer(C) ->
|
stop_bouncer(C) ->
|
||||||
ct_helper:with_config(group_apps, C,
|
ct_helper:with_config(
|
||||||
fun (Apps) -> genlib_app:stop_unload_applications(Apps) end).
|
group_apps,
|
||||||
|
C,
|
||||||
-spec init_per_testcase(atom(), config()) ->
|
fun(Apps) -> genlib_app:stop_unload_applications(Apps) end
|
||||||
config().
|
).
|
||||||
|
|
||||||
|
-spec init_per_testcase(atom(), config()) -> config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
[{testcase, Name} | C].
|
[{testcase, Name} | C].
|
||||||
|
|
||||||
-spec end_per_testcase(atom(), config()) ->
|
-spec end_per_testcase(atom(), config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
end_per_testcase(_Name, _C) ->
|
end_per_testcase(_Name, _C) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
-export([request_timeout_means_unknown/1]).
|
-export([request_timeout_means_unknown/1]).
|
||||||
|
|
||||||
-behaviour(bouncer_arbiter_pulse).
|
-behaviour(bouncer_arbiter_pulse).
|
||||||
|
|
||||||
-export([handle_beat/3]).
|
-export([handle_beat/3]).
|
||||||
|
|
||||||
-include_lib("bouncer_proto/include/bouncer_decisions_thrift.hrl").
|
-include_lib("bouncer_proto/include/bouncer_decisions_thrift.hrl").
|
||||||
@ -47,9 +48,7 @@
|
|||||||
-define(OPA_ENDPOINT, {?OPA_HOST, 8181}).
|
-define(OPA_ENDPOINT, {?OPA_HOST, 8181}).
|
||||||
-define(API_RULESET_ID, "service/authz/api").
|
-define(API_RULESET_ID, "service/authz/api").
|
||||||
|
|
||||||
-spec all() ->
|
-spec all() -> [atom()].
|
||||||
[atom()].
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[
|
[
|
||||||
{group, general},
|
{group, general},
|
||||||
@ -57,8 +56,7 @@ all() ->
|
|||||||
{group, network_error_mapping}
|
{group, network_error_mapping}
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec groups() ->
|
-spec groups() -> [{group_name(), list(), [test_case_name()]}].
|
||||||
[{group_name(), list(), [test_case_name()]}].
|
|
||||||
groups() ->
|
groups() ->
|
||||||
[
|
[
|
||||||
{general, [parallel], [
|
{general, [parallel], [
|
||||||
@ -84,28 +82,21 @@ groups() ->
|
|||||||
]}
|
]}
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec init_per_suite(config()) ->
|
-spec init_per_suite(config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
init_per_suite(C) ->
|
init_per_suite(C) ->
|
||||||
Apps =
|
Apps =
|
||||||
genlib_app:start_application(woody) ++
|
genlib_app:start_application(woody) ++
|
||||||
genlib_app:start_application_with(scoper, [
|
genlib_app:start_application_with(scoper, [
|
||||||
{storage, scoper_storage_logger}
|
{storage, scoper_storage_logger}
|
||||||
]),
|
]),
|
||||||
[{suite_apps, Apps} | C].
|
[{suite_apps, Apps} | C].
|
||||||
|
|
||||||
-spec end_per_suite(config()) ->
|
-spec end_per_suite(config()) -> ok.
|
||||||
ok.
|
|
||||||
end_per_suite(C) ->
|
end_per_suite(C) ->
|
||||||
genlib_app:stop_unload_applications(?CONFIG(suite_apps, C)).
|
genlib_app:stop_unload_applications(?CONFIG(suite_apps, C)).
|
||||||
|
|
||||||
-spec init_per_group(group_name(), config()) ->
|
-spec init_per_group(group_name(), config()) -> config().
|
||||||
config().
|
init_per_group(Name, C) when Name == general; Name == rules_authz_api ->
|
||||||
init_per_group(Name, C) when
|
|
||||||
Name == general;
|
|
||||||
Name == rules_authz_api
|
|
||||||
->
|
|
||||||
start_bouncer([], [{groupname, Name} | C]);
|
start_bouncer([], [{groupname, Name} | C]);
|
||||||
init_per_group(Name, C) ->
|
init_per_group(Name, C) ->
|
||||||
[{groupname, Name} | C].
|
[{groupname, Name} | C].
|
||||||
@ -115,24 +106,27 @@ start_bouncer(Env, C) ->
|
|||||||
Port = 8022,
|
Port = 8022,
|
||||||
ArbiterPath = <<"/v1/arbiter">>,
|
ArbiterPath = <<"/v1/arbiter">>,
|
||||||
{ok, StashPid} = ct_stash:start(),
|
{ok, StashPid} = ct_stash:start(),
|
||||||
Apps = genlib_app:start_application_with(bouncer, [
|
Apps = genlib_app:start_application_with(
|
||||||
{ip, IP},
|
bouncer,
|
||||||
{port, Port},
|
[
|
||||||
{services, #{
|
{ip, IP},
|
||||||
arbiter => #{
|
{port, Port},
|
||||||
path => ArbiterPath,
|
{services, #{
|
||||||
pulse => [{?MODULE, StashPid}]
|
arbiter => #{
|
||||||
}
|
path => ArbiterPath,
|
||||||
}},
|
pulse => [{?MODULE, StashPid}]
|
||||||
{transport_opts, #{
|
}
|
||||||
max_connections => 1000,
|
}},
|
||||||
num_acceptors => 4
|
{transport_opts, #{
|
||||||
}},
|
max_connections => 1000,
|
||||||
{opa, #{
|
num_acceptors => 4
|
||||||
endpoint => ?OPA_ENDPOINT,
|
}},
|
||||||
transport => tcp
|
{opa, #{
|
||||||
}}
|
endpoint => ?OPA_ENDPOINT,
|
||||||
] ++ Env),
|
transport => tcp
|
||||||
|
}}
|
||||||
|
] ++ Env
|
||||||
|
),
|
||||||
Services = #{
|
Services = #{
|
||||||
arbiter => mk_url(IP, Port, ArbiterPath)
|
arbiter => mk_url(IP, Port, ArbiterPath)
|
||||||
},
|
},
|
||||||
@ -141,34 +135,34 @@ start_bouncer(Env, C) ->
|
|||||||
mk_url(IP, Port, Path) ->
|
mk_url(IP, Port, Path) ->
|
||||||
iolist_to_binary(["http://", IP, ":", genlib:to_binary(Port), Path]).
|
iolist_to_binary(["http://", IP, ":", genlib:to_binary(Port), Path]).
|
||||||
|
|
||||||
-spec end_per_group(group_name(), config()) ->
|
-spec end_per_group(group_name(), config()) -> _.
|
||||||
_.
|
|
||||||
end_per_group(_Name, C) ->
|
end_per_group(_Name, C) ->
|
||||||
stop_bouncer(C).
|
stop_bouncer(C).
|
||||||
|
|
||||||
stop_bouncer(C) ->
|
stop_bouncer(C) ->
|
||||||
ct_helper:with_config(group_apps, C,
|
ct_helper:with_config(
|
||||||
fun (Apps) -> genlib_app:stop_unload_applications(Apps) end),
|
group_apps,
|
||||||
ct_helper:with_config(stash, C,
|
C,
|
||||||
fun (Pid) -> ?assertEqual(ok, ct_stash:destroy(Pid)) end).
|
fun(Apps) -> genlib_app:stop_unload_applications(Apps) end
|
||||||
|
),
|
||||||
-spec init_per_testcase(atom(), config()) ->
|
ct_helper:with_config(
|
||||||
config().
|
stash,
|
||||||
|
C,
|
||||||
|
fun(Pid) -> ?assertEqual(ok, ct_stash:destroy(Pid)) end
|
||||||
|
).
|
||||||
|
|
||||||
|
-spec init_per_testcase(atom(), config()) -> config().
|
||||||
init_per_testcase(Name, C) ->
|
init_per_testcase(Name, C) ->
|
||||||
[{testcase, Name} | C].
|
[{testcase, Name} | C].
|
||||||
|
|
||||||
-spec end_per_testcase(atom(), config()) ->
|
-spec end_per_testcase(atom(), config()) -> config().
|
||||||
config().
|
|
||||||
|
|
||||||
end_per_testcase(_Name, _C) ->
|
end_per_testcase(_Name, _C) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-define(CONTEXT(Fragments), #bdcs_Context{fragments = Fragments}).
|
-define(CONTEXT(Fragments), #bdcs_Context{fragments = Fragments}).
|
||||||
-define(JUDGEMENT(Resolution),
|
-define(JUDGEMENT(Resolution), #bdcs_Judgement{resolution = Resolution}).
|
||||||
#bdcs_Judgement{resolution = Resolution}).
|
|
||||||
|
|
||||||
-spec missing_ruleset_notfound(config()) -> ok.
|
-spec missing_ruleset_notfound(config()) -> ok.
|
||||||
-spec incorrect_ruleset_invalid1(config()) -> ok.
|
-spec incorrect_ruleset_invalid1(config()) -> ok.
|
||||||
@ -198,9 +192,11 @@ incorrect_ruleset_invalid1(C) ->
|
|||||||
call_judge("trivial/incorrect1", ?CONTEXT(#{}), Client)
|
call_judge("trivial/incorrect1", ?CONTEXT(#{}), Client)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{judgement, {failed, {ruleset_invalid, [
|
{judgement,
|
||||||
{data_invalid, _, wrong_size, _, [<<"resolution">>]}
|
{failed,
|
||||||
]}}},
|
{ruleset_invalid, [
|
||||||
|
{data_invalid, _, wrong_size, _, [<<"resolution">>]}
|
||||||
|
]}}},
|
||||||
lists:last(flush_beats(Client, C))
|
lists:last(flush_beats(Client, C))
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -211,9 +207,11 @@ incorrect_ruleset_invalid2(C) ->
|
|||||||
call_judge("trivial/incorrect2", ?CONTEXT(#{}), Client)
|
call_judge("trivial/incorrect2", ?CONTEXT(#{}), Client)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{judgement, {failed, {ruleset_invalid, [
|
{judgement,
|
||||||
{data_invalid, _, wrong_type, _, [<<"resolution">>, _]}
|
{failed,
|
||||||
]}}},
|
{ruleset_invalid, [
|
||||||
|
{data_invalid, _, wrong_type, _, [<<"resolution">>, _]}
|
||||||
|
]}}},
|
||||||
lists:last(flush_beats(Client, C))
|
lists:last(flush_beats(Client, C))
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -224,9 +222,11 @@ incorrect_ruleset_invalid3(C) ->
|
|||||||
call_judge("trivial/incorrect3", ?CONTEXT(#{}), Client)
|
call_judge("trivial/incorrect3", ?CONTEXT(#{}), Client)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{judgement, {failed, {ruleset_invalid, [
|
{judgement,
|
||||||
{data_invalid, _, no_extra_items_allowed, [<<"forbidden">>, [#{}], #{}], _}
|
{failed,
|
||||||
]}}},
|
{ruleset_invalid, [
|
||||||
|
{data_invalid, _, no_extra_items_allowed, [<<"forbidden">>, [#{}], #{}], _}
|
||||||
|
]}}},
|
||||||
lists:last(flush_beats(Client, C))
|
lists:last(flush_beats(Client, C))
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -239,9 +239,11 @@ missing_content_invalid_context(C) ->
|
|||||||
call_judge(?API_RULESET_ID, Context, Client)
|
call_judge(?API_RULESET_ID, Context, Client)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{judgement, {failed, {malformed_context, #{
|
{judgement,
|
||||||
<<"missing">> := {v1_thrift_binary, {unexpected, _, _, _}}
|
{failed,
|
||||||
}}}},
|
{malformed_context, #{
|
||||||
|
<<"missing">> := {v1_thrift_binary, {unexpected, _, _, _}}
|
||||||
|
}}}},
|
||||||
lists:last(flush_beats(Client, C))
|
lists:last(flush_beats(Client, C))
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -255,9 +257,11 @@ junk_content_invalid_context(C) ->
|
|||||||
call_judge(?API_RULESET_ID, Context, Client)
|
call_judge(?API_RULESET_ID, Context, Client)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
{judgement, {failed, {malformed_context, #{
|
{judgement,
|
||||||
<<"missing">> := {v1_thrift_binary, {unexpected, _, _, _}}
|
{failed,
|
||||||
}}}},
|
{malformed_context, #{
|
||||||
|
<<"missing">> := {v1_thrift_binary, {unexpected, _, _, _}}
|
||||||
|
}}}},
|
||||||
lists:last(flush_beats(Client, C))
|
lists:last(flush_beats(Client, C))
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -265,7 +269,7 @@ conflicting_context_invalid(C) ->
|
|||||||
Client = mk_client(C),
|
Client = mk_client(C),
|
||||||
Fragment1 = #{
|
Fragment1 = #{
|
||||||
user => #{
|
user => #{
|
||||||
id => <<"joeblow">>,
|
id => <<"joeblow">>,
|
||||||
email => Email1 = <<"deadinside69@example.org">>
|
email => Email1 = <<"deadinside69@example.org">>
|
||||||
},
|
},
|
||||||
requester => #{
|
requester => #{
|
||||||
@ -274,7 +278,7 @@ conflicting_context_invalid(C) ->
|
|||||||
},
|
},
|
||||||
Fragment2 = #{
|
Fragment2 = #{
|
||||||
user => #{
|
user => #{
|
||||||
id => <<"joeblow">>,
|
id => <<"joeblow">>,
|
||||||
email => <<"deadinside420@example.org">>
|
email => <<"deadinside420@example.org">>
|
||||||
},
|
},
|
||||||
requester => #{
|
requester => #{
|
||||||
@ -290,9 +294,11 @@ conflicting_context_invalid(C) ->
|
|||||||
call_judge(?API_RULESET_ID, Context, Client)
|
call_judge(?API_RULESET_ID, Context, Client)
|
||||||
),
|
),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
{judgement, {failed, {conflicting_context, #{
|
{judgement,
|
||||||
<<"frag2">> => #{user => #{email => Email1}}
|
{failed,
|
||||||
}}}},
|
{conflicting_context, #{
|
||||||
|
<<"frag2">> => #{user => #{email => Email1}}
|
||||||
|
}}}},
|
||||||
lists:last(flush_beats(Client, C))
|
lists:last(flush_beats(Client, C))
|
||||||
).
|
).
|
||||||
|
|
||||||
@ -300,7 +306,7 @@ distinct_sets_context_valid(C) ->
|
|||||||
Client = mk_client(C),
|
Client = mk_client(C),
|
||||||
Fragment1 = #{
|
Fragment1 = #{
|
||||||
user => #{
|
user => #{
|
||||||
id => <<"joeblow">>,
|
id => <<"joeblow">>,
|
||||||
orgs => mk_ordset([
|
orgs => mk_ordset([
|
||||||
#{
|
#{
|
||||||
id => <<"hoolie">>,
|
id => <<"hoolie">>,
|
||||||
@ -315,7 +321,7 @@ distinct_sets_context_valid(C) ->
|
|||||||
},
|
},
|
||||||
Fragment2 = #{
|
Fragment2 = #{
|
||||||
user => #{
|
user => #{
|
||||||
id => <<"joeblow">>,
|
id => <<"joeblow">>,
|
||||||
orgs => mk_ordset([
|
orgs => mk_ordset([
|
||||||
#{
|
#{
|
||||||
id => <<"hoolie">>,
|
id => <<"hoolie">>,
|
||||||
@ -354,11 +360,18 @@ restricted_search_invoices_shop_manager(C) ->
|
|||||||
mk_auth_session_token(),
|
mk_auth_session_token(),
|
||||||
mk_env(),
|
mk_env(),
|
||||||
mk_op_search_invoices(mk_ordset([#{id => <<"SHOP">>}]), <<"PARTY">>),
|
mk_op_search_invoices(mk_ordset([#{id => <<"SHOP">>}]), <<"PARTY">>),
|
||||||
mk_user(<<"USER">>, mk_ordset([
|
mk_user(
|
||||||
mk_user_org(<<"PARTY">>, <<"OWNER">>, mk_ordset([
|
<<"USER">>,
|
||||||
mk_role(<<"Manager">>, <<"SHOP">>)
|
mk_ordset([
|
||||||
]))
|
mk_user_org(
|
||||||
]))
|
<<"PARTY">>,
|
||||||
|
<<"OWNER">>,
|
||||||
|
mk_ordset([
|
||||||
|
mk_role(<<"Manager">>, <<"SHOP">>)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
])
|
||||||
|
)
|
||||||
]),
|
]),
|
||||||
Context = ?CONTEXT(#{<<"root">> => mk_ctx_v1_fragment(Fragment)}),
|
Context = ?CONTEXT(#{<<"root">> => mk_ctx_v1_fragment(Fragment)}),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
@ -375,8 +388,9 @@ forbidden_expired(C) ->
|
|||||||
% Would be funny if this fails on some system too deep in the past.
|
% Would be funny if this fails on some system too deep in the past.
|
||||||
Fragment = maps:merge(mk_env(), #{
|
Fragment = maps:merge(mk_env(), #{
|
||||||
auth => #{
|
auth => #{
|
||||||
method => <<"AccessToken">>,
|
method => <<"AccessToken">>,
|
||||||
expiration => <<"1991-12-26T17:00:00Z">> % ☭😢
|
% ☭😢
|
||||||
|
expiration => <<"1991-12-26T17:00:00Z">>
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Context = ?CONTEXT(#{<<"root">> => mk_ctx_v1_fragment(Fragment)}),
|
Context = ?CONTEXT(#{<<"root">> => mk_ctx_v1_fragment(Fragment)}),
|
||||||
@ -429,10 +443,12 @@ forbidden_w_empty_context(C) ->
|
|||||||
).
|
).
|
||||||
|
|
||||||
mk_user(UserID, UserOrgs) ->
|
mk_user(UserID, UserOrgs) ->
|
||||||
#{user => #{
|
#{
|
||||||
id => UserID,
|
user => #{
|
||||||
orgs => UserOrgs
|
id => UserID,
|
||||||
}}.
|
orgs => UserOrgs
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
mk_user_org(OrgID, OwnerID, Roles) ->
|
mk_user_org(OrgID, OwnerID, Roles) ->
|
||||||
#{
|
#{
|
||||||
@ -448,24 +464,30 @@ mk_auth_session_token() ->
|
|||||||
mk_auth_session_token(erlang:system_time(second) + 3600).
|
mk_auth_session_token(erlang:system_time(second) + 3600).
|
||||||
|
|
||||||
mk_auth_session_token(ExpiresAt) ->
|
mk_auth_session_token(ExpiresAt) ->
|
||||||
#{auth => #{
|
#{
|
||||||
method => <<"SessionToken">>,
|
auth => #{
|
||||||
expiration => format_ts(ExpiresAt, second)
|
method => <<"SessionToken">>,
|
||||||
}}.
|
expiration => format_ts(ExpiresAt, second)
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
mk_op_search_invoices(Shops, PartyID) ->
|
mk_op_search_invoices(Shops, PartyID) ->
|
||||||
#{anapi => #{
|
#{
|
||||||
op => #{
|
anapi => #{
|
||||||
id => <<"SearchInvoices">>,
|
op => #{
|
||||||
shops => Shops,
|
id => <<"SearchInvoices">>,
|
||||||
party => #{id => PartyID}
|
shops => Shops,
|
||||||
|
party => #{id => PartyID}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}.
|
}.
|
||||||
|
|
||||||
mk_env() ->
|
mk_env() ->
|
||||||
#{env => #{
|
#{
|
||||||
now => format_now()
|
env => #{
|
||||||
}}.
|
now => format_now()
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
format_now() ->
|
format_now() ->
|
||||||
USec = erlang:system_time(second),
|
USec = erlang:system_time(second),
|
||||||
@ -482,11 +504,16 @@ format_ts(Ts, Unit) ->
|
|||||||
-spec request_timeout_means_unknown(config()) -> ok.
|
-spec request_timeout_means_unknown(config()) -> ok.
|
||||||
|
|
||||||
connect_failed_means_unavailable(C) ->
|
connect_failed_means_unavailable(C) ->
|
||||||
C1 = start_bouncer([{opa, #{
|
C1 = start_bouncer(
|
||||||
endpoint => {?OPA_HOST, 65535},
|
[
|
||||||
transport => tcp,
|
{opa, #{
|
||||||
event_handler => {ct_gun_event_h, []}
|
endpoint => {?OPA_HOST, 65535},
|
||||||
}}], C),
|
transport => tcp,
|
||||||
|
event_handler => {ct_gun_event_h, []}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
),
|
||||||
Client = mk_client(C1),
|
Client = mk_client(C1),
|
||||||
try
|
try
|
||||||
?assertError(
|
?assertError(
|
||||||
@ -550,16 +577,24 @@ request_timeout_means_unknown(C) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
start_proxy_bouncer(Proxy, C) ->
|
start_proxy_bouncer(Proxy, C) ->
|
||||||
start_bouncer([{opa, #{
|
start_bouncer(
|
||||||
endpoint => ct_proxy:endpoint(Proxy),
|
[
|
||||||
transport => tcp,
|
{opa, #{
|
||||||
event_handler => {ct_gun_event_h, []}
|
endpoint => ct_proxy:endpoint(Proxy),
|
||||||
}}], C).
|
transport => tcp,
|
||||||
|
event_handler => {ct_gun_event_h, []}
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
C
|
||||||
|
).
|
||||||
|
|
||||||
change_proxy_mode(Proxy, Scope, Mode, C) ->
|
change_proxy_mode(Proxy, Scope, Mode, C) ->
|
||||||
ModeWas = ct_proxy:mode(Proxy, Scope, Mode),
|
ModeWas = ct_proxy:mode(Proxy, Scope, Mode),
|
||||||
_ = ct:pal(debug, "[~p] set proxy ~p from '~p' to '~p'",
|
_ = ct:pal(
|
||||||
[?CONFIG(testcase, C), Scope, ModeWas, Mode]),
|
debug,
|
||||||
|
"[~p] set proxy ~p from '~p' to '~p'",
|
||||||
|
[?CONFIG(testcase, C), Scope, ModeWas, Mode]
|
||||||
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@ -599,8 +634,7 @@ get_service_spec(arbiter) ->
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec handle_beat(bouncer_arbiter_pulse:beat(), bouncer_arbiter_pulse:metadata(), pid()) ->
|
-spec handle_beat(bouncer_arbiter_pulse:beat(), bouncer_arbiter_pulse:metadata(), pid()) -> ok.
|
||||||
ok.
|
|
||||||
handle_beat(Beat, Metadata, StashPid) ->
|
handle_beat(Beat, Metadata, StashPid) ->
|
||||||
_ = stash_beat(Beat, Metadata, StashPid),
|
_ = stash_beat(Beat, Metadata, StashPid),
|
||||||
ct:pal("~p [arbiter] ~0p:~nmetadata=~p", [self(), Beat, Metadata]).
|
ct:pal("~p [arbiter] ~0p:~nmetadata=~p", [self(), Beat, Metadata]).
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
-module(ct_gun_event_h).
|
-module(ct_gun_event_h).
|
||||||
|
|
||||||
-behavior(gun_event).
|
-behavior(gun_event).
|
||||||
|
|
||||||
-export([init/2]).
|
-export([init/2]).
|
||||||
@ -33,176 +34,147 @@
|
|||||||
|
|
||||||
-type st() :: _.
|
-type st() :: _.
|
||||||
|
|
||||||
-spec init(gun_event:init_event(), st()) ->
|
-spec init(gun_event:init_event(), st()) -> st().
|
||||||
st().
|
|
||||||
init(Event, State) ->
|
init(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] init: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] init: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec domain_lookup_start(gun_event:domain_lookup_event(), st()) ->
|
-spec domain_lookup_start(gun_event:domain_lookup_event(), st()) -> st().
|
||||||
st().
|
|
||||||
domain_lookup_start(Event, State) ->
|
domain_lookup_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] domain lookup start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] domain lookup start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec domain_lookup_end(gun_event:domain_lookup_event(), st()) ->
|
-spec domain_lookup_end(gun_event:domain_lookup_event(), st()) -> st().
|
||||||
st().
|
|
||||||
domain_lookup_end(Event, State) ->
|
domain_lookup_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] domain lookup end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] domain lookup end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec connect_start(gun_event:connect_event(), st()) ->
|
-spec connect_start(gun_event:connect_event(), st()) -> st().
|
||||||
st().
|
|
||||||
connect_start(Event, State) ->
|
connect_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] connect start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] connect start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec connect_end(gun_event:connect_event(), st()) ->
|
-spec connect_end(gun_event:connect_event(), st()) -> st().
|
||||||
st().
|
|
||||||
connect_end(Event, State) ->
|
connect_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] connect end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] connect end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec tls_handshake_start(gun_event:tls_handshake_event(), st()) ->
|
-spec tls_handshake_start(gun_event:tls_handshake_event(), st()) -> st().
|
||||||
st().
|
|
||||||
tls_handshake_start(Event, State) ->
|
tls_handshake_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] tls handshake start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] tls handshake start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec tls_handshake_end(gun_event:tls_handshake_event(), st()) ->
|
-spec tls_handshake_end(gun_event:tls_handshake_event(), st()) -> st().
|
||||||
st().
|
|
||||||
tls_handshake_end(Event, State) ->
|
tls_handshake_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] tls handshake end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] tls handshake end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec request_start(gun_event:request_start_event(), st()) ->
|
-spec request_start(gun_event:request_start_event(), st()) -> st().
|
||||||
st().
|
|
||||||
request_start(Event, State) ->
|
request_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] request start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] request start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec request_headers(gun_event:request_start_event(), st()) ->
|
-spec request_headers(gun_event:request_start_event(), st()) -> st().
|
||||||
st().
|
|
||||||
request_headers(Event, State) ->
|
request_headers(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] request headers: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] request headers: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec request_end(gun_event:request_end_event(), st()) ->
|
-spec request_end(gun_event:request_end_event(), st()) -> st().
|
||||||
st().
|
|
||||||
request_end(Event, State) ->
|
request_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] request end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] request end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec push_promise_start(gun_event:push_promise_start_event(), st()) ->
|
-spec push_promise_start(gun_event:push_promise_start_event(), st()) -> st().
|
||||||
st().
|
|
||||||
push_promise_start(Event, State) ->
|
push_promise_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] push promise start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] push promise start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec push_promise_end(gun_event:push_promise_end_event(), st()) ->
|
-spec push_promise_end(gun_event:push_promise_end_event(), st()) -> st().
|
||||||
st().
|
|
||||||
push_promise_end(Event, State) ->
|
push_promise_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] push promise end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] push promise end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec response_start(gun_event:response_start_event(), st()) ->
|
-spec response_start(gun_event:response_start_event(), st()) -> st().
|
||||||
st().
|
|
||||||
response_start(Event, State) ->
|
response_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] response start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] response start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec response_inform(gun_event:response_headers_event(), st()) ->
|
-spec response_inform(gun_event:response_headers_event(), st()) -> st().
|
||||||
st().
|
|
||||||
response_inform(Event, State) ->
|
response_inform(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] response inform: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] response inform: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec response_headers(gun_event:response_headers_event(), st()) ->
|
-spec response_headers(gun_event:response_headers_event(), st()) -> st().
|
||||||
st().
|
|
||||||
response_headers(Event, State) ->
|
response_headers(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] response headers: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] response headers: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec response_trailers(gun_event:response_trailers_event(), st()) ->
|
-spec response_trailers(gun_event:response_trailers_event(), st()) -> st().
|
||||||
st().
|
|
||||||
response_trailers(Event, State) ->
|
response_trailers(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] response trailers: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] response trailers: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec response_end(gun_event:response_end_event(), st()) ->
|
-spec response_end(gun_event:response_end_event(), st()) -> st().
|
||||||
st().
|
|
||||||
response_end(Event, State) ->
|
response_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] response end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] response end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec ws_upgrade(gun_event:ws_upgrade_event(), st()) ->
|
-spec ws_upgrade(gun_event:ws_upgrade_event(), st()) -> st().
|
||||||
st().
|
|
||||||
ws_upgrade(Event, State) ->
|
ws_upgrade(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] ws upgrade: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] ws upgrade: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec ws_recv_frame_start(gun_event:ws_recv_frame_start_event(), st()) ->
|
-spec ws_recv_frame_start(gun_event:ws_recv_frame_start_event(), st()) -> st().
|
||||||
st().
|
|
||||||
ws_recv_frame_start(Event, State) ->
|
ws_recv_frame_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] ws recv frame start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] ws recv frame start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec ws_recv_frame_header(gun_event:ws_recv_frame_header_event(), st()) ->
|
-spec ws_recv_frame_header(gun_event:ws_recv_frame_header_event(), st()) -> st().
|
||||||
st().
|
|
||||||
ws_recv_frame_header(Event, State) ->
|
ws_recv_frame_header(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] ws recv frame header: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] ws recv frame header: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec ws_recv_frame_end(gun_event:ws_recv_frame_end_event(), st()) ->
|
-spec ws_recv_frame_end(gun_event:ws_recv_frame_end_event(), st()) -> st().
|
||||||
st().
|
|
||||||
ws_recv_frame_end(Event, State) ->
|
ws_recv_frame_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] ws recv frame end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] ws recv frame end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec ws_send_frame_start(gun_event:ws_send_frame_event(), st()) ->
|
-spec ws_send_frame_start(gun_event:ws_send_frame_event(), st()) -> st().
|
||||||
st().
|
|
||||||
ws_send_frame_start(Event, State) ->
|
ws_send_frame_start(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] ws send frame start: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] ws send frame start: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec ws_send_frame_end(gun_event:ws_send_frame_event(), st()) ->
|
-spec ws_send_frame_end(gun_event:ws_send_frame_event(), st()) -> st().
|
||||||
st().
|
|
||||||
ws_send_frame_end(Event, State) ->
|
ws_send_frame_end(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] ws send frame end: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] ws send frame end: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec protocol_changed(gun_event:protocol_changed_event(), st()) ->
|
-spec protocol_changed(gun_event:protocol_changed_event(), st()) -> st().
|
||||||
st().
|
|
||||||
protocol_changed(Event, State) ->
|
protocol_changed(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] protocol changed: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] protocol changed: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec transport_changed(gun_event:transport_changed_event(), st()) ->
|
-spec transport_changed(gun_event:transport_changed_event(), st()) -> st().
|
||||||
st().
|
|
||||||
transport_changed(Event, State) ->
|
transport_changed(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] transport changed: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] transport changed: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec origin_changed(gun_event:origin_changed_event(), st()) ->
|
-spec origin_changed(gun_event:origin_changed_event(), st()) -> st().
|
||||||
st().
|
|
||||||
origin_changed(Event, State) ->
|
origin_changed(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] origin changed: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] origin changed: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec cancel(gun_event:cancel_event(), st()) ->
|
-spec cancel(gun_event:cancel_event(), st()) -> st().
|
||||||
st().
|
|
||||||
cancel(Event, State) ->
|
cancel(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] cancel: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] cancel: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec disconnect(gun_event:disconnect_event(), st()) ->
|
-spec disconnect(gun_event:disconnect_event(), st()) -> st().
|
||||||
st().
|
|
||||||
disconnect(Event, State) ->
|
disconnect(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] disconnect: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] disconnect: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec terminate(gun_event:terminate_event(), st()) ->
|
-spec terminate(gun_event:terminate_event(), st()) -> st().
|
||||||
st().
|
|
||||||
terminate(Event, State) ->
|
terminate(Event, State) ->
|
||||||
_ = ct:pal("~p [gun] terminate: ~p", [self(), Event]),
|
_ = ct:pal("~p [gun] terminate: ~p", [self(), Event]),
|
||||||
State.
|
State.
|
||||||
|
@ -11,26 +11,25 @@
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-spec with_config(atom(), config(), fun ((_) -> R)) ->
|
-spec with_config(atom(), config(), fun((_) -> R)) -> R | undefined.
|
||||||
R | undefined.
|
|
||||||
with_config(Name, C, Fun) ->
|
with_config(Name, C, Fun) ->
|
||||||
case lists:keyfind(Name, 1, C) of
|
case lists:keyfind(Name, 1, C) of
|
||||||
{_, V} -> Fun(V);
|
{_, V} -> Fun(V);
|
||||||
false -> undefined
|
false -> undefined
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_temp_dir() ->
|
-spec get_temp_dir() -> file:filename_all().
|
||||||
file:filename_all().
|
|
||||||
get_temp_dir() ->
|
get_temp_dir() ->
|
||||||
hd(genlib_list:compact([
|
hd(
|
||||||
get_env("TMPDIR"),
|
genlib_list:compact([
|
||||||
get_env("TEMP"),
|
get_env("TMPDIR"),
|
||||||
get_env("TMP"),
|
get_env("TEMP"),
|
||||||
"/tmp"
|
get_env("TMP"),
|
||||||
])).
|
"/tmp"
|
||||||
|
])
|
||||||
|
).
|
||||||
|
|
||||||
-spec get_env(string()) ->
|
-spec get_env(string()) -> string() | undefined.
|
||||||
string() | undefined.
|
|
||||||
get_env(Name) ->
|
get_env(Name) ->
|
||||||
case os:getenv(Name) of
|
case os:getenv(Name) of
|
||||||
V when is_list(V) ->
|
V when is_list(V) ->
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
init/1,
|
init/1,
|
||||||
handle_call/3,
|
handle_call/3,
|
||||||
@ -25,23 +26,19 @@
|
|||||||
-include_lib("kernel/include/inet.hrl").
|
-include_lib("kernel/include/inet.hrl").
|
||||||
|
|
||||||
-type endpoint() :: {inet:hostname(), inet:port_number()}.
|
-type endpoint() :: {inet:hostname(), inet:port_number()}.
|
||||||
-type scope() :: listen | connection.
|
-type scope() :: listen | connection.
|
||||||
-type mode() :: ignore | stop | relay.
|
-type mode() :: ignore | stop | relay.
|
||||||
-type modes() :: #{scope() => mode()}.
|
-type modes() :: #{scope() => mode()}.
|
||||||
|
|
||||||
-type proxy() :: pid().
|
-type proxy() :: pid().
|
||||||
|
|
||||||
-spec start_link(endpoint()) ->
|
-spec start_link(endpoint()) -> {ok, proxy()}.
|
||||||
{ok, proxy()}.
|
|
||||||
|
|
||||||
-spec start_link(endpoint(), modes()) ->
|
-spec start_link(endpoint(), modes()) -> {ok, proxy()}.
|
||||||
{ok, proxy()}.
|
|
||||||
|
|
||||||
-spec start_link(endpoint(), modes(), ranch_tcp:opts()) ->
|
-spec start_link(endpoint(), modes(), ranch_tcp:opts()) -> {ok, proxy()}.
|
||||||
{ok, proxy()}.
|
|
||||||
|
|
||||||
-spec unlink(proxy()) ->
|
-spec unlink(proxy()) -> proxy().
|
||||||
proxy().
|
|
||||||
|
|
||||||
start_link(Upstream) ->
|
start_link(Upstream) ->
|
||||||
start_link(Upstream, #{}).
|
start_link(Upstream, #{}).
|
||||||
@ -61,44 +58,35 @@ unlink(Proxy) when is_pid(Proxy) ->
|
|||||||
true = erlang:unlink(Proxy),
|
true = erlang:unlink(Proxy),
|
||||||
Proxy.
|
Proxy.
|
||||||
|
|
||||||
-spec endpoint(proxy()) ->
|
-spec endpoint(proxy()) -> endpoint().
|
||||||
endpoint().
|
|
||||||
endpoint(Proxy) when is_pid(Proxy) ->
|
endpoint(Proxy) when is_pid(Proxy) ->
|
||||||
gen_server:call(Proxy, endpoint).
|
gen_server:call(Proxy, endpoint).
|
||||||
|
|
||||||
-spec mode(proxy(), scope()) ->
|
-spec mode(proxy(), scope()) -> {mode(), _Upstream :: endpoint()}.
|
||||||
{mode(), _Upstream :: endpoint()}.
|
|
||||||
|
|
||||||
mode(Proxy, Scope) when is_pid(Proxy) ->
|
mode(Proxy, Scope) when is_pid(Proxy) ->
|
||||||
gen_server:call(Proxy, {mode, Scope}).
|
gen_server:call(Proxy, {mode, Scope}).
|
||||||
|
|
||||||
-spec mode(proxy(), scope(), mode()) ->
|
-spec mode(proxy(), scope(), mode()) -> mode().
|
||||||
mode().
|
|
||||||
|
|
||||||
mode(Proxy, Scope, Mode) when is_pid(Proxy) ->
|
mode(Proxy, Scope, Mode) when is_pid(Proxy) ->
|
||||||
gen_server:call(Proxy, {mode, Scope, Mode}).
|
gen_server:call(Proxy, {mode, Scope, Mode}).
|
||||||
|
|
||||||
-spec stop(proxy()) ->
|
-spec stop(proxy()) -> ok.
|
||||||
ok.
|
|
||||||
|
|
||||||
stop(Proxy) when is_pid(Proxy) ->
|
stop(Proxy) when is_pid(Proxy) ->
|
||||||
proc_lib:stop(Proxy, shutdown).
|
proc_lib:stop(Proxy, shutdown).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
-record(st, {
|
-record(st, {
|
||||||
lsock :: _Socket | undefined,
|
lsock :: _Socket | undefined,
|
||||||
lsockopts :: list(),
|
lsockopts :: list(),
|
||||||
acceptor :: pid() | undefined,
|
acceptor :: pid() | undefined,
|
||||||
modes :: #{scope() => mode()},
|
modes :: #{scope() => mode()},
|
||||||
upstream :: {inet:ip_address(), inet:port_number()}
|
upstream :: {inet:ip_address(), inet:port_number()}
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type st() :: #st{}.
|
-type st() :: #st{}.
|
||||||
|
|
||||||
-spec init(_) ->
|
-spec init(_) -> {ok, st()}.
|
||||||
{ok, st()}.
|
|
||||||
|
|
||||||
init({Upstream, Modes0, SocketOpts}) ->
|
init({Upstream, Modes0, SocketOpts}) ->
|
||||||
Modes = maps:merge(#{listen => relay, connection => relay}, Modes0),
|
Modes = maps:merge(#{listen => relay, connection => relay}, Modes0),
|
||||||
St = #st{
|
St = #st{
|
||||||
@ -108,9 +96,7 @@ init({Upstream, Modes0, SocketOpts}) ->
|
|||||||
},
|
},
|
||||||
{ok, sync_mode(listen, stop, maps:get(listen, Modes), St)}.
|
{ok, sync_mode(listen, stop, maps:get(listen, Modes), St)}.
|
||||||
|
|
||||||
-spec handle_call(_Call, _From, st()) ->
|
-spec handle_call(_Call, _From, st()) -> {noreply, st()}.
|
||||||
{noreply, st()}.
|
|
||||||
|
|
||||||
handle_call(endpoint, _From, St = #st{}) ->
|
handle_call(endpoint, _From, St = #st{}) ->
|
||||||
{reply, get_endpoint(St), St};
|
{reply, get_endpoint(St), St};
|
||||||
handle_call({mode, Scope, Mode}, _From, St = #st{modes = Modes}) ->
|
handle_call({mode, Scope, Mode}, _From, St = #st{modes = Modes}) ->
|
||||||
@ -122,27 +108,19 @@ handle_call({mode, Scope}, _From, St = #st{modes = Modes, upstream = Endpoint})
|
|||||||
handle_call(_Call, _From, St) ->
|
handle_call(_Call, _From, St) ->
|
||||||
{noreply, St}.
|
{noreply, St}.
|
||||||
|
|
||||||
-spec handle_cast(_Cast, st()) ->
|
-spec handle_cast(_Cast, st()) -> {noreply, st()}.
|
||||||
{noreply, st()}.
|
|
||||||
|
|
||||||
handle_cast(_Cast, St) ->
|
handle_cast(_Cast, St) ->
|
||||||
{noreply, St}.
|
{noreply, St}.
|
||||||
|
|
||||||
-spec handle_info(_Info, st()) ->
|
-spec handle_info(_Info, st()) -> {noreply, st()}.
|
||||||
{noreply, st()}.
|
|
||||||
|
|
||||||
handle_info(_Info, St) ->
|
handle_info(_Info, St) ->
|
||||||
{noreply, St}.
|
{noreply, St}.
|
||||||
|
|
||||||
-spec terminate(_Reason, st()) ->
|
-spec terminate(_Reason, st()) -> _.
|
||||||
_.
|
|
||||||
|
|
||||||
terminate(_Reason, _St) ->
|
terminate(_Reason, _St) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec code_change(_Vsn | {down, _Vsn}, st(), _Extra) ->
|
-spec code_change(_Vsn | {down, _Vsn}, st(), _Extra) -> {ok, st()}.
|
||||||
{ok, st()}.
|
|
||||||
|
|
||||||
code_change(_Vsn, St, _Extra) ->
|
code_change(_Vsn, St, _Extra) ->
|
||||||
{ok, St}.
|
{ok, St}.
|
||||||
|
|
||||||
@ -190,7 +168,7 @@ stop_listener(St = #st{lsock = LSock}) when lsock /= undefined ->
|
|||||||
start_acceptor(St = #st{acceptor = undefined, lsock = LSock}) ->
|
start_acceptor(St = #st{acceptor = undefined, lsock = LSock}) ->
|
||||||
ct:pal("start_acceptor @ ~p", [St]),
|
ct:pal("start_acceptor @ ~p", [St]),
|
||||||
Parent = self(),
|
Parent = self(),
|
||||||
Pid = erlang:spawn_link(fun () -> loop_acceptor(Parent, LSock) end),
|
Pid = erlang:spawn_link(fun() -> loop_acceptor(Parent, LSock) end),
|
||||||
St#st{acceptor = Pid}.
|
St#st{acceptor = Pid}.
|
||||||
|
|
||||||
stop_acceptor(St = #st{acceptor = Pid}) when is_pid(Pid) ->
|
stop_acceptor(St = #st{acceptor = Pid}) when is_pid(Pid) ->
|
||||||
@ -198,19 +176,21 @@ stop_acceptor(St = #st{acceptor = Pid}) when is_pid(Pid) ->
|
|||||||
MRef = erlang:monitor(process, Pid),
|
MRef = erlang:monitor(process, Pid),
|
||||||
true = erlang:unlink(Pid),
|
true = erlang:unlink(Pid),
|
||||||
true = erlang:exit(Pid, shutdown),
|
true = erlang:exit(Pid, shutdown),
|
||||||
receive {'DOWN', MRef, process, Pid, _Reason} ->
|
receive
|
||||||
St#st{acceptor = undefined}
|
{'DOWN', MRef, process, Pid, _Reason} ->
|
||||||
|
St#st{acceptor = undefined}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
loop_acceptor(Parent, LSock) ->
|
loop_acceptor(Parent, LSock) ->
|
||||||
_ = case ranch_tcp:accept(LSock, infinity) of
|
_ =
|
||||||
{ok, CSock} ->
|
case ranch_tcp:accept(LSock, infinity) of
|
||||||
_ = ct:pal("accepted ~p from ~p", [CSock, ranch_tcp:peername(CSock)]),
|
{ok, CSock} ->
|
||||||
_ = spawn_proxy_connection(Parent, CSock),
|
_ = ct:pal("accepted ~p from ~p", [CSock, ranch_tcp:peername(CSock)]),
|
||||||
loop_acceptor(Parent, LSock);
|
_ = spawn_proxy_connection(Parent, CSock),
|
||||||
{error, Reason} ->
|
loop_acceptor(Parent, LSock);
|
||||||
exit(Reason)
|
{error, Reason} ->
|
||||||
end.
|
exit(Reason)
|
||||||
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -228,7 +208,7 @@ loop_acceptor(Parent, LSock) ->
|
|||||||
|
|
||||||
spawn_proxy_connection(Parent, CSock) ->
|
spawn_proxy_connection(Parent, CSock) ->
|
||||||
ProxySt = #proxy{insock = CSock, parent = Parent},
|
ProxySt = #proxy{insock = CSock, parent = Parent},
|
||||||
erlang:spawn_link(fun () -> loop_proxy_connection(ProxySt) end).
|
erlang:spawn_link(fun() -> loop_proxy_connection(ProxySt) end).
|
||||||
|
|
||||||
loop_proxy_connection(St = #proxy{insock = InSock, parent = Parent, buffer = Buffer}) ->
|
loop_proxy_connection(St = #proxy{insock = InSock, parent = Parent, buffer = Buffer}) ->
|
||||||
case ranch_tcp:recv(InSock, 0, ?PROXY_RECV_TIMEOUT) of
|
case ranch_tcp:recv(InSock, 0, ?PROXY_RECV_TIMEOUT) of
|
||||||
@ -241,7 +221,7 @@ loop_proxy_connection(St = #proxy{insock = InSock, parent = Parent, buffer = Buf
|
|||||||
ignore ->
|
ignore ->
|
||||||
loop_proxy_connection(St);
|
loop_proxy_connection(St);
|
||||||
relay ->
|
relay ->
|
||||||
loop_proxy_relay(St#proxy{buffer = Buffer1, upstream = Endpoint})
|
loop_proxy_relay(St#proxy{buffer = Buffer1, upstream = Endpoint})
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
terminate(St)
|
terminate(St)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
-module(ct_stash).
|
-module(ct_stash).
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-export([start/0]).
|
-export([start/0]).
|
||||||
@ -22,23 +23,19 @@
|
|||||||
-type key() :: _.
|
-type key() :: _.
|
||||||
-type entry() :: _.
|
-type entry() :: _.
|
||||||
|
|
||||||
-spec start() ->
|
-spec start() -> {ok, pid()}.
|
||||||
{ok, pid()}.
|
|
||||||
start() ->
|
start() ->
|
||||||
gen_server:start(?MODULE, [], []).
|
gen_server:start(?MODULE, [], []).
|
||||||
|
|
||||||
-spec destroy(pid()) ->
|
-spec destroy(pid()) -> ok | {error, {nonempty, _Left :: #{key() => entry()}}}.
|
||||||
ok | {error, {nonempty, _Left :: #{key() => entry()}}}.
|
|
||||||
destroy(Pid) ->
|
destroy(Pid) ->
|
||||||
call(Pid, destroy).
|
call(Pid, destroy).
|
||||||
|
|
||||||
-spec append(pid(), key(), entry()) ->
|
-spec append(pid(), key(), entry()) -> ok.
|
||||||
ok.
|
|
||||||
append(Pid, Key, Entry) ->
|
append(Pid, Key, Entry) ->
|
||||||
call(Pid, {append, Key, Entry}).
|
call(Pid, {append, Key, Entry}).
|
||||||
|
|
||||||
-spec flush(pid(), key()) ->
|
-spec flush(pid(), key()) -> {ok, [entry()]} | error.
|
||||||
{ok, [entry()]} | error.
|
|
||||||
flush(Pid, Key) ->
|
flush(Pid, Key) ->
|
||||||
call(Pid, {flush, Key}).
|
call(Pid, {flush, Key}).
|
||||||
|
|
||||||
@ -47,13 +44,11 @@ call(Pid, Msg) ->
|
|||||||
|
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
|
|
||||||
-spec init(term()) ->
|
-spec init(term()) -> {ok, atom()}.
|
||||||
{ok, atom()}.
|
|
||||||
init(_) ->
|
init(_) ->
|
||||||
{ok, #{}}.
|
{ok, #{}}.
|
||||||
|
|
||||||
-spec handle_call(term(), pid(), atom()) ->
|
-spec handle_call(term(), pid(), atom()) -> {reply, atom(), atom()}.
|
||||||
{reply, atom(), atom()}.
|
|
||||||
handle_call({append, Key, Entry}, _From, State) ->
|
handle_call({append, Key, Entry}, _From, State) ->
|
||||||
Entries = maps:get(Key, State, []),
|
Entries = maps:get(Key, State, []),
|
||||||
State1 = maps:put(Key, [Entry | Entries], State),
|
State1 = maps:put(Key, [Entry | Entries], State),
|
||||||
@ -70,27 +65,23 @@ handle_call(destroy, _From, State) ->
|
|||||||
0 ->
|
0 ->
|
||||||
{stop, shutdown, ok, State};
|
{stop, shutdown, ok, State};
|
||||||
_ ->
|
_ ->
|
||||||
Left = maps:map(fun (_, Entries) -> lists:reverse(Entries) end, State),
|
Left = maps:map(fun(_, Entries) -> lists:reverse(Entries) end, State),
|
||||||
Reason = {error, {nonempty, Left}},
|
Reason = {error, {nonempty, Left}},
|
||||||
{stop, Reason, Reason, State}
|
{stop, Reason, Reason, State}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec handle_cast(term(), atom()) -> {noreply, atom()}.
|
-spec handle_cast(term(), atom()) -> {noreply, atom()}.
|
||||||
|
|
||||||
handle_cast(_Msg, State) ->
|
handle_cast(_Msg, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
-spec handle_info(term(), atom()) -> {noreply, atom()}.
|
-spec handle_info(term(), atom()) -> {noreply, atom()}.
|
||||||
|
|
||||||
handle_info(_Info, State) ->
|
handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
-spec terminate(term(), atom()) -> atom().
|
-spec terminate(term(), atom()) -> atom().
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec code_change(term(), term(), term()) -> {ok, atom()}.
|
-spec code_change(term(), term(), term()) -> {ok, atom()}.
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
Loading…
Reference in New Issue
Block a user