diff --git a/Makefile b/Makefile index b5ee9911a886b56f44eb6e11d5481e8db5d514da..f0ef9e5ec6322965484b8b93cfbc53f3fd7d656a 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ compile: @$(REBAR) compile eunit: - @FORMAT="compact" ERL_AFLAGS="-args_file config/test-vm.args -config config/test-sys.config" $(REBAR) as test do eunit --cover --application=consul_proxy --dir=apps/consul_proxy/test, cover --verbose + $(REBAR) as test do eunit --cover --application=consul_proxy --dir=apps/consul_proxy/test, cover --verbose dialyzer: @$(REBAR) dialyzer diff --git a/apps/consul_proxy/priv/Dockerfile b/apps/consul_proxy/priv/Dockerfile index 6364e316c4dd816c9dbe8ceb7ef0a661d59677da..b312bc9818b71e467fd4252839383aee233499bd 100644 --- a/apps/consul_proxy/priv/Dockerfile +++ b/apps/consul_proxy/priv/Dockerfile @@ -1,12 +1,12 @@ -FROM hedenstroem/otp +FROM hedenstroem/otp:19.1 MAINTAINER Erik Hedenström <erik@erlang.ninja> -ENV NAME_PREFIX consul_proxy +ENV NAME_PREFIX {{release_name}} ENV COOKIE Nax7jEj7bay6ril -ADD rel/consul_proxy/consul_proxy-${REL_VSN}.tar.gz /opt/consul_proxy +ADD {{release_name}}-{{rel_vsn}}.tar.gz /opt/{{release_name}} EXPOSE 8080-8083 -ENTRYPOINT [ "/opt/consul_proxy/bin/consul_proxy-wrapper" ] +ENTRYPOINT [ "/opt/{{release_name}}/bin/wrapper" ] CMD [ "foreground" ] diff --git a/apps/consul_proxy/priv/consul_proxy-wrapper b/apps/consul_proxy/priv/wrapper similarity index 84% rename from apps/consul_proxy/priv/consul_proxy-wrapper rename to apps/consul_proxy/priv/wrapper index 7de6a3eb7f0a21926a45966f348a2c1418ebdde7..a4c26e902b0f8e57d438db8b1c876c48151365a9 100755 --- a/apps/consul_proxy/priv/consul_proxy-wrapper +++ b/apps/consul_proxy/priv/wrapper @@ -9,7 +9,7 @@ fi; SCRIPT_DIR="$(cd `dirname "$SCRIPT"` && pwd -P)" if [ -z "$NAME_PREFIX" ]; then - NAME_PREFIX="consul_proxy-$(openssl rand -hex 4)" + NAME_PREFIX="{{release_name}}-$(openssl rand -hex 4)" fi if [ -z "$COOKIE" ]; then @@ -25,4 +25,4 @@ export NODE_NAME=${NAME_PREFIX}@${HOST_IP} echo "Starting ${NODE_NAME} with cookie ${COOKIE}" -${SCRIPT_DIR}/consul_proxy $@ +${SCRIPT_DIR}/{{release_name}} $@ diff --git a/apps/consul_proxy/src/consul_client.erl b/apps/consul_proxy/src/consul_client.erl index 46215a38119581c03c35a552989a3701e40344af..ef9be505145e447a86bbedbe1e29038ea72042af 100644 --- a/apps/consul_proxy/src/consul_client.erl +++ b/apps/consul_proxy/src/consul_client.erl @@ -503,7 +503,7 @@ watch(Parent, Path, PathWithIndex, Delay) -> -spec trigger_webhooks(Path :: binary, Index :: number()) -> any(). trigger_webhooks(<<"/v1/kv/consul_proxy?recurse">>, Index) -> - trigger_webhooks(<<"consul_proxy/watchers/kv">>, Index, []); + trigger_webhooks(<<"consul_proxy/watchers/kv">>, Index, [{<<"index">>, Index}]); trigger_webhooks(<<"/v1/catalog/services">>, Index) -> case consul_client:services() of {ok, Services} -> diff --git a/apps/consul_proxy/src/consul_proxy.app.src b/apps/consul_proxy/src/consul_proxy.app.src index 548d36bed4fe641b26990cad9afee2bf8b35cf0b..8bc1ff176f5816b47f93b3acfe6e8c692b7742b5 100644 --- a/apps/consul_proxy/src/consul_proxy.app.src +++ b/apps/consul_proxy/src/consul_proxy.app.src @@ -1,6 +1,6 @@ {application, consul_proxy, [ {description, "Proxy for docker swarm using consul and vegur"}, - {vsn, "0.5.3"}, + {vsn, "0.5.4"}, {registered, []}, {mod, {consul_proxy_app, []}}, {applications, [ diff --git a/apps/consul_proxy/src/consul_proxy_lager_backend.erl b/apps/consul_proxy/src/consul_proxy_lager_backend.erl index 825e1e9df03184e996891eddbaa6981e0e082c0e..b5fb8ab733b8ed8352ccedd098c9c450ec765152 100644 --- a/apps/consul_proxy/src/consul_proxy_lager_backend.erl +++ b/apps/consul_proxy/src/consul_proxy_lager_backend.erl @@ -7,7 +7,6 @@ -record(state, {level, formatter, config, socket}). init(Config) -> - random:seed(erlang:phash2([node()]), erlang:monotonic_time(), erlang:unique_integer()), Level = proplists:get_value(level, Config, info), Formatter = proplists:get_value(formatter, Config, consul_proxy_request_formatter), Levels = lager_util:config_to_mask(Level), @@ -69,14 +68,14 @@ pick_node([]) -> pick_node([Node]) -> Node; pick_node(Nodes) -> - N = random:uniform(length(Nodes)), + N = rand:uniform(length(Nodes)), lists:nth(N, Nodes). %% @private lookup_nodes() -> case consul_proxy_utils:cache_get(logservice) of undefined -> - Service = application:get_env(consul_proxy, logservice, "logstash-udp"), + Service = application:get_env(consul_proxy, logservice, "logstash"), case consul_client:service(Service) of {ok, List} -> Nodes = lists:map( diff --git a/apps/consul_proxy/src/consul_proxy_loadbalancer.erl b/apps/consul_proxy/src/consul_proxy_loadbalancer.erl index 7cd0623e9e4064be18f103598e1e3f08b2942c53..39fb699e566559df2adcf689d6ecf24b32bfbb5b 100644 --- a/apps/consul_proxy/src/consul_proxy_loadbalancer.erl +++ b/apps/consul_proxy/src/consul_proxy_loadbalancer.erl @@ -97,7 +97,6 @@ start_link(Args) -> {stop, Reason :: term()} | ignore). init(Args) -> lager:info("Args: ~p", [Args]), - random:seed(erlang:phash2([node()]), erlang:monotonic_time(), erlang:unique_integer()), Counter = tsuru_counter:new(), ForwardDecays = ets:new(forward_decays, [set, {write_concurrency, true}]), Weights = ets:new(weights, [set, {write_concurrency, true}]), @@ -118,7 +117,7 @@ init(Args) -> {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | {stop, Reason :: term(), NewState :: #state{}}). handle_call({pick, random, Services}, _From, State) -> - Index = random:uniform(length(Services)), + Index = rand:uniform(length(Services)), Pick = lists:nth(Index, Services), {reply, {ok, Pick, lists:delete(Pick, Services)}, State}; @@ -129,7 +128,7 @@ handle_call({pick, jsq, Services}, _From, State = #state{counter = Counter}) -> {Service, get_count(Counter, Service), NewServices}; (Candidate, {Current, CurrentCount, NewServices}) -> CandidateCount = get_count(Counter, Candidate), - OneOrTwo = random:uniform(2), + OneOrTwo = rand:uniform(2), if CandidateCount < CurrentCount -> {Candidate, CandidateCount, [Current | NewServices]}; CandidateCount > CurrentCount -> {Current, CurrentCount, [Candidate | NewServices]}; @@ -147,7 +146,7 @@ handle_call({pick, esl, Services}, _From, State) -> {Candidate, Weight, []}; ({Candidate, Weight}, {Current, Sum, NewServices}) -> NewSum = Sum + Weight, - R = random:uniform(NewSum), + R = rand:uniform(NewSum), if R > Sum -> {Candidate, NewSum, [Current | NewServices]}; @@ -158,19 +157,19 @@ handle_call({pick, esl, Services}, _From, State) -> {reply, {ok, Pick, ServicesExcludingPick}, State}; handle_call({sort, random, Services}, _From, State) -> - ShuffledServices = [R || {_, R} <- lists:sort([{random:uniform(), S} || S <- Services])], + ShuffledServices = [R || {_, R} <- lists:sort([{rand:uniform(), S} || S <- Services])], {reply, {ok, ShuffledServices}, State}; handle_call({sort, jsq, Services}, _From, State = #state{counter = Counter}) -> OrderingFun = fun({A, _}, {B, _}) -> A =< B end, - ShuffledServices = [R || {_, R} <- lists:sort(OrderingFun, [{random:uniform(), S} || S <- Services])], + ShuffledServices = [R || {_, R} <- lists:sort(OrderingFun, [{rand:uniform(), S} || S <- Services])], WeightedServices = [{get_count(Counter, Service), Service} || Service <- ShuffledServices], SortedServices = [R || {_, R} <- lists:sort(OrderingFun, WeightedServices)], {reply, {ok, SortedServices}, State}; handle_call({sort, esl, Services}, _From, State) -> OrderingFun = fun({A, _}, {B, _}) -> A =< B end, - ShuffledServices = [R || {_, R} <- lists:sort(OrderingFun, [{random:uniform(), S} || S <- Services])], + ShuffledServices = [R || {_, R} <- lists:sort(OrderingFun, [{rand:uniform(), S} || S <- Services])], WeightedServices = [{get_weight(State, Service), Service} || Service <- ShuffledServices], SortedServices = [R || {_, R} <- lists:sort(OrderingFun, WeightedServices)], {reply, {ok, SortedServices}, State}; diff --git a/apps/consul_proxy/src/consul_proxy_router.erl b/apps/consul_proxy/src/consul_proxy_router.erl index 25395655498df67776a74dcc8aa3d392a569fb96..3baacc3992fd19a7dd667f85a5a07710a9c5c28b 100644 --- a/apps/consul_proxy/src/consul_proxy_router.erl +++ b/apps/consul_proxy/src/consul_proxy_router.erl @@ -16,8 +16,7 @@ -record(state, {services = undefined, headers = []}). -init(AcceptTime, Upstream) -> - random:seed(AcceptTime), +init(_AcceptTime, Upstream) -> {ok, Upstream, #state{}}. lookup_domain_name(Domain, Upstream, HandlerState) -> diff --git a/apps/consul_proxy/test/common_steps.erl b/apps/consul_proxy/test/common_steps.erl index e2f758ccfe71ee6b995cc3ebf54fc2a878ed4913..8a28a1d5694137952cf5a77cf5ba87e8274d17a9 100644 --- a/apps/consul_proxy/test/common_steps.erl +++ b/apps/consul_proxy/test/common_steps.erl @@ -25,26 +25,20 @@ setup_feature(_Tokens) -> {K, V} end, os:getenv()), lager:debug("~p", [Env]), - application:ensure_all_started(inets), - application:ensure_all_started(nkdocker), - {_Pid, State} = get_nkdocker_pid(#{os_env => Env, test_env => [], nkdocker_ids => []}), - {ok, State}. + application:ensure_all_started(hackney), + {ok, Pid} = dockerl:start_link(socket, <<"/var/run/docker.sock">>), + IPAddress = list_to_binary(inet:ntoa(local_ip_v4())), + {ok, #{os_env => Env, test_env => [{<<"ip_address">>, IPAddress}], applications => [], containers => [], dockerl_pid => Pid}}. -teardown_feature(#{nkdocker_pid := Pid, nkdocker_ids := Ids}) -> - {async, Ref} = nkdocker:events(Pid), +teardown_feature(#{dockerl_pid := Pid, containers := Containers, applications := Applications}) -> + [application:stop(Application) || Application <- lists:reverse(Applications)], lists:foreach( fun({Name, Id}) -> - ok = nkdocker:kill(Pid, Id), - nkdocker_receive_status(Ref, Id, <<"kill">>), - nkdocker_receive_status(Ref, Id, <<"die">>), - lager:notice("Docker container ~s killed", [Name]), - ok = nkdocker:rm(Pid, Id), - nkdocker_receive_status(Ref, Id, <<"destroy">>), - lager:notice("Docker container ~s destroyed", [Name]) - end, Ids), - nkdocker:finish_async(Pid, Ref), - nkdocker_receive(Ref, {ok, user_stop}), - nkdocker:stop(Pid), + ok = dockerl:stop_container(Pid, Id), + lager:notice("Docker container ~s stopped", [Name]), + ok = dockerl:remove_container(Pid, Id), + lager:notice("Docker container ~s removed", [Name]) + end, Containers), ok; teardown_feature(_State) -> ok. @@ -56,63 +50,67 @@ teardown_scenario(_State) -> ok. %% noinspection ErlangUnboundVariable -given([<<"a">>, <<"docker">>, <<"container">>, <<"named">>, Name, <<"running">>, Image, <<"with">>, <<"commands:">>, {docstring, Args}], State = #{nkdocker_pid := Pid, nkdocker_ids := Ids}) -> +given([<<"a">>, <<"docker">>, <<"container">>, <<"named">>, Name, <<"running">>, Image, <<"with">>, <<"commands:">>, {docstring, Args}], State = #{dockerl_pid := Pid, containers := Containers}) -> InterpolatedImage = consul_proxy_utils:string_interpolate(Image, maps:get(os_env, State, []) ++ maps:get(test_env, State, []), []), - {async, Ref} = nkdocker:events(Pid), - {ok, #{<<"Id">> := Id}} = nkdocker:create(Pid, InterpolatedImage, + {ok, _} = dockerl:pull_image(Pid, Image), + {ok, Id} = dockerl:create_container(Pid, Image, #{ - publish_all => true, - cmds => binary:split(Args, <<" ">>, [trim_all, global]) + 'Tty' => true, + 'Cmd' => binary:split(Args, <<" ">>, [trim_all, global]), + 'PublishAllPorts' => true }), - nkdocker_receive_status(Ref, Id, <<"create">>), lager:notice("Docker container ~s running ~s created: ~s", [Name, InterpolatedImage, Id]), - ok = nkdocker:start(Pid, Id), - nkdocker_receive_status(Ref, Id, <<"start">>), - lager:notice("Docker container ~s started", [Name]), - nkdocker:finish_async(Pid, Ref), - nkdocker_receive(Ref, {ok, user_stop}), - {ok, State#{nkdocker_ids => [{Name, Id} | Ids]}}; - -given("$Name logs match $Pattern", State = #{nkdocker_pid := Pid, nkdocker_ids := Ids}) -> - Id = proplists:get_value(Name, Ids), - {async, Ref} = nkdocker:logs(Pid, Id, #{stdout=>true, follow=>true}), - {match, _Captured} = nkdocker_receive_match(Ref, Pattern), - nkdocker:finish_async(Pid, Ref), - nkdocker_receive(Ref, {ok, user_stop}), + ok = dockerl:start_container(Pid, Id), + lager:notice("Docker container ~s started", [Id]), + {ok, State#{containers => [{Name, Id} | Containers]}}; + +given("$Name logs match $Pattern", State = #{dockerl_pid := Pid, containers := Containers}) -> + Id = proplists:get_value(Name, Containers), + {ok, Stream} = dockerl:container_logs(Pid, Id), + ok = match_logs(Stream, Pattern), {ok, State}; -given("consul_proxy is connected to $Name on port $Port", State = #{nkdocker_pid := Pid, nkdocker_ids := Ids}) -> - Id = proplists:get_value(Name, Ids), - {ok, Data} = nkdocker:inspect(Pid, Id), - NetworkSettings = maps:get(<<"NetworkSettings">>, Data), - IPAddress = maps:get(<<"IPAddress">>, NetworkSettings), - URL = binary_to_list(<<"http://", IPAddress/binary, ":", Port/binary>>), +given("consul_proxy is connected to $Name port $Port", State = #{dockerl_pid := Pid, containers := Containers, applications := Applications}) -> + Id = proplists:get_value(Name, Containers), + {ok, Ports} = dockerl_utils:get_ports(Pid, Id), + [{_, IntPort}] = maps:get(Port, Ports), + BinPort = integer_to_binary(IntPort), + BinAddress = case os:type() of + {unix, darwin} -> %% Todo: remove this hack once routing is fixed in Docker for Mac + <<"127.0.0.1">>; + _ -> + {ok, Gateway} = dockerl_utils:get_gateway(Pid, Id), + list_to_binary(inet:ntoa(Gateway)) + end, + URL = binary_to_list(<<"http://", BinAddress/binary, ":", BinPort/binary>>), + lager:notice("Consul URL: ~s", [URL]), ok = application:set_env(consul_proxy, consul_urls, URL, [{timeout, infinity}, {persistent, true}]), case application:ensure_all_started(consul_proxy) of {ok, Started} -> lager:notice("Started ~p applications: ~p on ~p~n", [erlang:length(Started), Started, node()]), {ok, HttpcPid} = inets:start(httpc, [{profile, consul_proxied}]), httpc:set_options([{proxy, {{"localhost", 8080}, []}}], consul_proxied), - {ok, State#{consul_proxied_httpc => HttpcPid}}; + {ok, State#{consul_proxied_httpc => HttpcPid, applications => Applications ++ Started}}; {error, Reason} -> lager:error("~p", [Reason]), {error, Reason} end; -given("a UDP listener registered as $ServiceName", State) -> - {ok, Socket} = gen_udp:open(0, [binary, {ip, {127, 0, 0, 1}}, {active, false}]), - {ok, Port} = inet:port(Socket), - BinPort = integer_to_binary(Port), - Value = <<"{\"Name\":\"", ServiceName/binary, "\",\"Address\":\"127.0.0.1\",\"Port\":", BinPort/binary, "}">>, +given("a UDP listener registered as $ServiceName", State = #{test_env := TestEnv}) -> + {ok, Socket} = gen_udp:open(0, [binary, {ip, {0, 0, 0, 0}}, {active, false}]), + {ok, IntPort} = inet:port(Socket), + BinPort = integer_to_binary(IntPort), + BinAddress = proplists:get_value(<<"ip_address">>, TestEnv), + Value = <<"{\"Name\":\"", ServiceName/binary, "\",\"Address\":\"", BinAddress/binary, "\",\"Port\":", BinPort/binary, "}">>, {ok, <<>>} = consul_client:http_put(<<"/v1/agent/service/register">>, Value), - lager:notice("UDP listener ~p started on ~p", [ServiceName, Port]), + lager:notice("UDP listener ~p started on ~p", [ServiceName, IntPort]), spawn(fun() -> udp_receive(Socket) end), - {ok, State#{test_env => [{<<ServiceName/binary, "_port">>, BinPort} | maps:get(test_env, State, [])]}}; + {ok, State#{test_env => [{<<ServiceName/binary, "_port">>, BinPort} | TestEnv]}}; -given("an HTTP Server with root $Root and handlers $Handlers registered as $ServiceName", State) -> +given("an HTTP Server with root $Root and handlers $Handlers registered as $ServiceName", State = #{test_env := TestEnv}) -> Modules = [binary_to_atom(Handler, utf8) || Handler <- binary:split(Handlers, <<",">>, [trim_all, global])], ServiceConfig = [ - {bind_address, "localhost"}, + {bind_address, "0.0.0.0"}, {port, 0}, {modules, [mod_esi, mod_dir, mod_get]}, {erl_script_alias, {"/erl", Modules}}, @@ -124,7 +122,8 @@ given("an HTTP Server with root $Root and handlers $Handlers registered as $Serv {ok, Pid} = inets:start(httpd, ServiceConfig), Port = proplists:get_value(port, httpd:info(Pid)), BinPort = integer_to_binary(Port), - Value = <<"{\"Name\":\"", ServiceName/binary, "\",\"Address\":\"127.0.0.1\",\"Port\":", BinPort/binary, "}">>, + BinAddress = proplists:get_value(<<"ip_address">>, TestEnv), + Value = <<"{\"Name\":\"", ServiceName/binary, "\",\"Address\":\"", BinAddress/binary, "\",\"Port\":", BinPort/binary, "}">>, {ok, <<>>} = consul_client:http_put(<<"/v1/agent/service/register">>, Value), lager:notice("HTTP Server ~p started on ~p", [ServiceName, Port]), {ok, State#{test_env => [{<<ServiceName/binary, "_port">>, BinPort} | maps:get(test_env, State, [])]}}; @@ -171,91 +170,22 @@ then(Tokens, _State) -> %%=================================================================== %% Internal functions %%=================================================================== -get_nkdocker_pid(State) -> - case maps:get(nkdocker_pid, State, undefined) of - undefined -> - {ok, Pid} = nkdocker:start_link(), - {ok, Version} = nkdocker:version(Pid), - lager:debug("Docker Version:~n~s", [io_lib_pretty:print(Version)]), - {Pid, State#{nkdocker_pid => Pid}}; - Pid -> - {Pid, State} - end. - -nkdocker_receive(Ref, Data) -> - receive - {nkdocker, Ref, Data} -> - lager:debug("~p", [Data]), - ok; - Other -> - lager:debug("~p", [Other]), - nkdocker_receive(Ref, Data) - after - 1000 -> - {error, timeout} - end. - -nkdocker_receive_status(Ref, Id, Status) -> - receive - {nkdocker, Ref, Data = {data, #{<<"id">>:=Id, <<"status">>:=Status}}} -> - lager:debug("~p", [Data]), - ok; - Other -> - lager:debug("~p", [Other]), - nkdocker_receive_status(Ref, Id, Status) - after - 1000 -> - {error, timeout} - end. - -nkdocker_receive_data(Ref) -> - nkdocker_receive_data(Ref, <<>>, 3000). - -nkdocker_receive_data(Ref, Buffer, Timeout) -> - receive - {ok, <<>>} -> - {ok, Buffer}; - {nkdocker, Ref, {data, Data}} when is_binary(Data) -> - lager:debug("~p", [Data]), - nkdocker_receive_data(Ref, <<Buffer/binary, Data/binary>>, Timeout); - {nkdocker, Ref, {data, Data}} -> - lager:debug("~p", [Data]), - nkdocker_receive_data(Ref, Buffer, Timeout); - Other -> - lager:debug("~p", [Other]), - nkdocker_receive_data(Ref, Buffer, Timeout) - after - Timeout -> - {error, timeout} - end. -nkdocker_receive_match(Ref, RE) -> - nkdocker_receive_match(Ref, RE, 3000). - -nkdocker_receive_match(Ref, Regexp, Timeout) when is_binary(Regexp) -> - case re:compile(Regexp) of - {ok, MP} -> - nkdocker_receive_match(Ref, MP, Timeout); - {error, Reason} -> - {error, Reason} - end; -nkdocker_receive_match(Ref, RE, Timeout) -> +match_logs(Stream, Pattern) -> receive - {ok, <<>>} -> - nomatch; - {nkdocker, Ref, {data, Data}} when is_binary(Data) -> - lager:debug("~p", [Data]), - case re:run(Data, RE) of - nomatch -> - nkdocker_receive_match(Ref, RE, Timeout); - Match -> - Match - end; - Other -> - lager:debug("~p", [Other]), - nkdocker_receive_match(Ref, RE, Timeout) + done -> + {error, no_match}; + Msg -> + case re:run(Msg, Pattern) of + {match, _} -> + Stream ! stop, + ok; + _ -> + match_logs(Stream, Pattern) + end after - Timeout -> + 10000 -> + Stream ! stop, {error, timeout} end. @@ -267,3 +197,10 @@ udp_receive(Socket) -> {error, _Reason} -> udp_receive(Socket) end. + +local_ip_v4() -> + {ok, Addrs} = inet:getifaddrs(), + hd([ + Addr || {_, Opts} <- Addrs, {addr, Addr} <- Opts, + size(Addr) == 4, Addr =/= {127, 0, 0, 1} + ]). diff --git a/apps/consul_proxy/test/feature_consul_proxy.erl b/apps/consul_proxy/test/feature_consul_proxy.erl index 9b8011b03b497b08e3b5f9148113f3511464fc0e..3ba9c3537d638a52749ab721ebaa0405b36515fb 100644 --- a/apps/consul_proxy/test/feature_consul_proxy.erl +++ b/apps/consul_proxy/test/feature_consul_proxy.erl @@ -37,12 +37,12 @@ given(Tokens, State) -> %% noinspection ErlangUnboundVariable 'when'("the consul client lists nodes", State) -> {ok, Nodes} = consul_client:nodes(), - lager:notice("~p", [Nodes]), + lager:notice("Nodes: ~p", [Nodes]), {ok, State#{result => Nodes}}; 'when'("the consul client lists services", State) -> {ok, Services} = consul_client:services(), - lager:notice("~p", [Services]), + lager:notice("Services: ~p", [Services]), {ok, State#{result => Services}}; 'when'("a request is made to $URL", State) -> diff --git a/apps/consul_proxy/test/features_test.erl b/apps/consul_proxy/test/features_test.erl index db2280754b15d6bc7c5ba75da0982e0bf720ac34..4c458351d2af29abf3fb87c29086125deff38f43 100644 --- a/apps/consul_proxy/test/features_test.erl +++ b/apps/consul_proxy/test/features_test.erl @@ -11,6 +11,14 @@ features_test_() -> gurka_eunit:setup(Features, fun teardown/1). setup() -> + case file:consult("config/test-sys.config") of + {ok, [ConfigList]} -> + [application:set_env(Application, Key, Val, [{timeout, infinity}, {persistent, true}]) || + {Application, Items} <- ConfigList, + {Key, Val} <- Items]; + {error, Reason} -> + ?debugFmt("~p: ~p", ["config/test-sys.config", Reason]) + end, lager:start(). teardown(_) -> diff --git a/config/test-sys.config b/config/test-sys.config index 6628c229a499d00750b4cf0f3a6b3ea58989f8b4..6a4769863b6b063db5a30bbd7741018cc41ce895 100644 --- a/config/test-sys.config +++ b/config/test-sys.config @@ -30,7 +30,7 @@ {crash_log_size, 10485760}, {crash_log_date, "$D0"}, {crash_log_count, 5}, - {error_logger_redirect, false} + {error_logger_redirect, true} ]}, {vegur, [ {downstream_connect_timeout, 1000}, diff --git a/config/test-vm.args b/config/test-vm.args deleted file mode 100644 index 1afe19d9c76a43e1a0ff07d4f3d3fdd86e9715a2..0000000000000000000000000000000000000000 --- a/config/test-vm.args +++ /dev/null @@ -1,16 +0,0 @@ -## Name of the node --sname consul_proxy - -## Enable kernel poll and a few async threads -+K true -+A 64 -+P 262144 - -## enable smp support --smp auto - -## Increase number of concurrent ports/sockets --env ERL_MAX_PORTS 16384 - -## Tweak GC to run more often --env ERL_FULLSWEEP_AFTER 10 diff --git a/features/consul_proxy.feature b/features/consul_proxy.feature index e5f5ddc555ae24b62861911d5630a9c42f496f76..d01d77c16baaf55624b86c0ffe5eafb26ee58c8f 100644 --- a/features/consul_proxy.feature +++ b/features/consul_proxy.feature @@ -9,25 +9,25 @@ Feature: Proxy functionality agent -dev -ui-dir /ui -client 0.0.0.0 """ And consul logs match "Synced service 'consul'" - And consul_proxy is connected to consul on port 8500 + And consul_proxy is connected to consul port 8500/tcp And a UDP listener registered as logstash And an HTTP Server with root "test/www" and handlers "eunit_www" registered as eunit And consul client has set consul_proxy/watchers/kv to: """ [ - "http://localhost:$<eunit_port>/erl/eunit_www:info" + "http://$<ip_address>:$<eunit_port>/erl/eunit_www:info" ] """ And consul client has set consul_proxy/watchers/services to: """ [ - "http://localhost:$<eunit_port>/erl/eunit_www:info" + "http://$<ip_address>:$<eunit_port>/erl/eunit_www:info" ] """ And consul client has set consul_proxy/watchers/nodes to: """ [ - "http://localhost:$<eunit_port>/erl/eunit_www:info" + "http://$<ip_address>:$<eunit_port>/erl/eunit_www:info" ] """ And consul client has set consul_proxy/domains/eunit to: diff --git a/rebar.config b/rebar.config index db2b06599bf7d514c8e32f0d0d7991f2e3e010e9..240dba8936353ed57758ff08af48d2af0fe3b472 100644 --- a/rebar.config +++ b/rebar.config @@ -1,5 +1,10 @@ {global_rebar_dir, ".rebar3"}. +{plugins, [ + {rebar_alias, "0.2.0"}, + {rebar_cmd, "0.2.5"} +]}. + {erl_opts, [{parse_transform, lager_transform}, {lager_extra_sinks, [request_log, latency_log]}]}. {eunit_opts, [{report, {eunit_surefire, [{dir, "_build/test"}]}}]}. @@ -7,18 +12,18 @@ {edoc_opts, [{dir, "_build/edoc"}]}. {deps, [ - {lager, {git, "https://github.com/basho/lager.git", {tag, "3.1.0"}}}, - {tsuru, "1.4.0"}, + {lager, "3.2.1"}, + {tsuru, "1.4.1"}, {msgpack, "0.4.0"}, {jsx, "2.8.0"}, {lru, "1.3.1"}, {pbkdf2, "2.0.0"}, {luerl, {git, "https://github.com/rvirding/luerl.git", {tag, "v0.3"}}}, - {vegur, {git, "https://github.com/heroku/vegur.git", {tag, "v1.1.1"}}} + {vegur, {git, "https://github.com/heroku/vegur.git", {tag, "2.0.3"}}} ]}. {relx, [ - {release, {consul_proxy, "0.5.3"}, [consul_proxy]}, + {release, {consul_proxy, "0.5.4"}, [consul_proxy]}, {sys_config, "./config/sys.config"}, {vm_args, "./config/vm.args"}, {dev_mode, true}, @@ -26,7 +31,8 @@ {system_libs, false}, {extended_start_script, true}, {overlay, [ - {copy, "apps/consul_proxy/priv/consul_proxy-wrapper", "bin/consul_proxy-wrapper"} + {template, "apps/consul_proxy/priv/wrapper", "bin/wrapper"}, + {template, "apps/consul_proxy/priv/Dockerfile", "Dockerfile"} ]} ]}. @@ -34,20 +40,56 @@ {test, [ {deps, [ {gurka, "0.1.7"}, - {gun, "1.0.0-pre.1"}, - {cowlib, {git, "https://github.com/ninenines/cowlib.git", {tag, "1.3.0"}}}, - {cowboy, ".*", {git, "https://github.com/extend/cowboy", {ref, "90ae31998e8d0887b9efe4b441136ac047708bb9"}}}, - {nkpacket, {git, "https://github.com/NetComposer/nkpacket.git", {ref, "b2869f68060f779da56f63d0ff5e8c58a011d32c"}}}, - {nkdocker, {git, "https://github.com/NetComposer/nkdocker.git", {ref, "f0fc3f866ecf9ded00c3eaaef2402afefc56f77f"}}} + {dockerl, {git, "https://gitlab.hedenstroem.com/erlang-ninja/dockerl.git", {tag, "0.1.0"}}} ]}, {eunit_opts, [{report, {eunit_surefire, [{dir, "_build/test"}]}}]}, {erl_opts, [debug_info, nowarn_unused_vars]} ]}, - {production, [ + {prod, [ + {erl_opts, [no_debug_info, warnings_as_errors]}, {relx, [ - {dev_mode, false}, - {include_erts, false} + {dev_mode, false} ]}, - {erl_opts, [no_debug_info, warnings_as_errors]} + {commands, [ + {docker, "docker build -t registry.hedenstroem.com/erlang-ninja/consul_proxy _build/prod/rel/consul_proxy"} + ]} ]} ]}. + +{dist_node, [ + {setcookie, 'cookie'}, + {sname, 'consul_proxy@localhost'} +]}. + +{shell, [ + {config, "config/test-sys.config"} +]}. + +{dialyzer, [ + {base_plt_location, global} +]}. + +{alias, [ + {test, [ + {eunit, "--cover --application=consul_proxy --dir=apps/consul_proxy/test"}, + {cover, "--verbose"} + ]}, + {analyze, [ + dialyzer, + xref + ]}, + {cleanup, [ + clean, + {cmd, "distclean"} + ]}, + {docker, [ + tar, + {cmd, "docker"} + ]} +]}. + +{commands, [ + {distclean, "rm -rf .rebar3 _build _checkouts/*/ebin ebin log"}, + {sync, "git fetch upstream && git merge upstream/master"}, + {docker, "docker build -t registry.hedenstroem.com/erlang-ninja/consul_proxy _build/default/rel/consul_proxy"} +]}. diff --git a/rebar.lock b/rebar.lock index 5ab9781755b16f98011b5fe0d9dd1e1cdaf4e1a0..7a41e87cd8593e92c40d8bd1aa79b3bedf37c7db 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,6 @@ [{<<"cowboyku">>, {git,"https://github.com/heroku/cowboyku.git", - {ref,"ca64d29abe15c1522ba77283f15de20677aa5889"}}, + {ref,"4acffec2be36d2ac66b79d4e9782de1213d1de4a"}}, 1}, {<<"cowlib">>, {git,"https://github.com/ninenines/cowlib.git", @@ -8,17 +8,11 @@ 1}, {<<"erequest_id">>, {git,"https://github.com/heroku/erequest_id.git", - {ref,"e24f250c119e14975eff038ac30b2f9f7d1ceefa"}}, - 1}, - {<<"goldrush">>, - {git,"git://github.com/DeadZen/goldrush.git", - {ref,"212299233c7e7eb63a97be2777e1c05ebaa58dbe"}}, + {ref,"6fae0c878f2dde809482915c8b207dc80882f7b3"}}, 1}, + {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.8">>},1}, {<<"jsx">>,{pkg,<<"jsx">>,<<"2.8.0">>},0}, - {<<"lager">>, - {git,"https://github.com/basho/lager.git", - {ref,"b2cb2735713e3021e0761623ff595d53a545438e"}}, - 0}, + {<<"lager">>,{pkg,<<"lager">>,<<"3.2.1">>},0}, {<<"lru">>,{pkg,<<"lru">>,<<"1.3.1">>},0}, {<<"luerl">>, {git,"https://github.com/rvirding/luerl.git", @@ -32,7 +26,7 @@ {<<"pbkdf2">>,{pkg,<<"pbkdf2">>,<<"2.0.0">>},0}, {<<"quickrand">>, {git,"https://github.com/okeuday/quickrand.git", - {ref,"cf7ac11a820367496252243e49898480b25009f7"}}, + {ref,"ad46bb697b98e8dcbf5c4a0af0099a5f1b2a0aa2"}}, 2}, {<<"ranch">>, {git,"https://github.com/ninenines/ranch.git", @@ -40,14 +34,14 @@ 1}, {<<"ranch_proxy_protocol">>, {git,"https://github.com/heroku/ranch_proxy_protocol.git", - {ref,"5110e6ca4bc25dfb11d4ecf8d003169b95bf8969"}}, + {ref,"0129227305a5b41f0875b337ac75f02f613f81c1"}}, 1}, - {<<"tsuru">>,{pkg,<<"tsuru">>,<<"1.4.0">>},0}, + {<<"tsuru">>,{pkg,<<"tsuru">>,<<"1.4.1">>},0}, {<<"uuid">>, {git,"https://github.com/okeuday/uuid.git", - {ref,"24325ae6309d03897735c3a320aab6f74b189ab7"}}, + {ref,"4b705b22f5d095970558d904e43c4354bd0e0a7f"}}, 1}, {<<"vegur">>, {git,"https://github.com/heroku/vegur.git", - {ref,"612d2de22bc6931dcc2bb0ae9c3e3187c4d77ca1"}}, + {ref,"038542b071c8d1591f3f7843759271be3ed4a99b"}}, 0}]. diff --git a/scripts/genpasswd.escript b/scripts/genpasswd.escript index 76f9f31729d78051f0aad4a064891f56cfd1cfa7..53e853889047717c7a6ac78c89db070ef7362b20 100755 --- a/scripts/genpasswd.escript +++ b/scripts/genpasswd.escript @@ -5,6 +5,8 @@ -define(PBKDF2_ITERATIONS, 4096). -define(PBKDF2_DERIVED_LENGTH, 32). +-export([main/1]). + main([Username, Password]) -> try Hash = base64:encode(hash(Password)), diff --git a/test/boot2docker-route.sh b/test/boot2docker-route.sh deleted file mode 100755 index f4ffdbc39f4f025fcdd153c9c2fe2408b2780a9a..0000000000000000000000000000000000000000 --- a/test/boot2docker-route.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -if [[ 'Running' != $(docker-machine status) ]]; then - echo "boot2docker's VM not running" - exit 1 -fi - -IP=$(docker-machine ip default 2> /dev/null) -for NET in $(docker-machine ssh default route | awk '/docker0|br-/{print $1}'); do - case $1 in - "add" ) - sudo route -n add "$NET" "$IP";; - "delete" ) - sudo route -n delete "$NET" "$IP";; - esac -done - -HOST_IP=$(docker-machine ssh default route | awk '/UG/{print $2}') - -echo "IP to host inside container: $HOST_IP" -netstat -rn | awk "\$2 == \"$IP\"||/Destination.*Refs/{print;}" diff --git a/test/consul/consul.json b/test/consul/consul.json new file mode 100644 index 0000000000000000000000000000000000000000..e2a462f90ae968e5db5c2e9258f9a1aa1ee2a545 --- /dev/null +++ b/test/consul/consul.json @@ -0,0 +1,10 @@ +{ + "data_dir": "/data", + "ui_dir": "/ui", + "client_addr": "0.0.0.0", + "recursors": ["8.8.8.8", "8.8.4.4"], + "disable_update_check": true, + "server" : true, + "rejoin_after_leave" : true, + "bootstrap_expect" : 1 +} diff --git a/test/defaults.consul b/test/defaults.consul index e1eb12a355d30ed616b664223444453f65d38c43..a6d317f8a8806afa46420de2f3dab9ab75b77e26 100644 --- a/test/defaults.consul +++ b/test/defaults.consul @@ -2,9 +2,9 @@ consul_proxy/domains/test.service.docker:ewogICJTZXJ2aWNlTmFtZSIgOiAidGVzdCIKfQ= consul_proxy/domains/consul.service.docker:ewogICJTZXJ2aWNlTmFtZSIgOiAiY29uc3VsIgp9 consul_proxy/domains/rewrite-example:ewogICJTZXJ2aWNlTmFtZSIgOiAiJDxzZXJ2aWNlPiIsCiAgIkFsaWFzZXMiIDogWwogICAgIig/PHNlcnZpY2U+LiopLXJld3JpdGVbLl1zZXJ2aWNlWy5dZG9ja2VyIgogIF0sCiAgIlJld3JpdGUiIDogWwogICAgWyJeL2FwaS8oLiopJCIsIi9pbmZvLnBocD9hcGk9JDEiXQogIF0KfQ== consul_proxy/scripts/resolver-example.erl:UmVxdWVzdCA9IHByb3BsaXN0czpnZXRfdmFsdWUocmVxdWVzdCwgUHJvcHMsIFtdKSwKTWV0YSA9IHByb3BsaXN0czpnZXRfdmFsdWUobWV0YSwgUmVxdWVzdCwgW10pLApDYXB0dXJlID0gcHJvcGxpc3RzOmdldF92YWx1ZShob3N0X2NhcHR1cmUsIE1ldGEsIFtdKSwKcHJvcGxpc3RzOmdldF92YWx1ZSg8PCJzZXJ2aWNlIj4+LCBDYXB0dXJlKS4= -consul_proxy/watchers/nodes:WwogICAgImh0dHBzOi8vdGVzdC5zZXJ2aWNlLmRvY2tlci9pbmZvLnBocCIKXQ== -consul_proxy/watchers/services:WwogICAgImh0dHBzOi8vdGVzdC5zZXJ2aWNlLmRvY2tlci9pbmZvLnBocCIKXQ== -consul_proxy/watchers/kv:WwogICAgImh0dHBzOi8vdGVzdC5zZXJ2aWNlLmRvY2tlci9pbmZvLnBocCIKXQ== +consul_proxy/watchers/nodes:WwogICAgImh0dHA6Ly9sb2dzdGFzaDo4MDgwL3dhdGNoL25vZGVzIgpd +consul_proxy/watchers/services:WwogICAgImh0dHA6Ly9sb2dzdGFzaDo4MDgwL3dhdGNoL3NlcnZpY2VzIgpd +consul_proxy/watchers/kv:WwogICAgImh0dHA6Ly9sb2dzdGFzaDo4MDgwL3dhdGNoL2t2Igpd consul_proxy/hijackers:WwogIHsKICAgICJ1c2VybmFtZSIgOiAiaGlqYWNrZXIiLAogICAgInBhc3N3b3JkIiA6ICIwdEhWdFNhOThrSGkyZStkVHVWdHo3S3lYWTgzVys3cGZ6eEZYd2VKWUtualcxOXRSeHBVbFFSV1lYbGI4V21YIiwKICAgICJkb21haW5zIiA6IFsKICAgICAgIi4qIgogICAgXQogIH0KXQ== consul_proxy/domains/resolver-example-erl:ewogICJTZXJ2aWNlUmVzb2x2ZXIiIDogInJlc29sdmVyLWV4YW1wbGUuZXJsIiwKICAiQWxpYXNlcyIgOiBbCiAgICAiKD88c2VydmljZT4uKiktcmVzb2x2ZXItZXJsWy5dc2VydmljZVsuXWRvY2tlciIKICBdCn0= consul_proxy/domains/resolver-example-lua:ewogICJTZXJ2aWNlUmVzb2x2ZXIiIDogInJlc29sdmVyLWV4YW1wbGUubHVhIiwKICAiQWxpYXNlcyIgOiBbCiAgICAiKD88c2VydmljZT4uKiktcmVzb2x2ZXItbHVhWy5dc2VydmljZVsuXWRvY2tlciIKICBdCn0= diff --git a/test/docker-compose.yml b/test/docker-compose.yml index 34d7a45776cbb26481b4eec1ea6210c4ef184385..c3278db6dd9a3bd7ed3b0d49a2c712988ab67c8e 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -3,8 +3,8 @@ version: '2' services: consul: - image: ehedenst/consul:x86 - command: agent -dev -ui-dir /ui -client 0.0.0.0 + image: hedenstroem/consul + command: agent -config-dir /config environment: - SERVICE_8300_IGNORE=true - SERVICE_8301_IGNORE=true @@ -15,9 +15,32 @@ services: ports: - "8500:8500" - "8600:8600/udp" + - "8600:8600/tcp" + volumes: + - ./consul:/config + + consul_proxy: + image: registry.hedenstroem.com/erlang-ninja/consul_proxy + links: + - consul + - logstash + environment: + - CONSUL_PROXY_LOGLEVEL=debug + - CONSUL_PROXY_HTTP_PORT=80 + - CONSUL_PROXY_CONSUL_URLS=http://consul:8500 + - CONSUL_PROXY_LOGSERVICE=logstash-udp + - CONSUL_PROXY_HIJACK_CERT=/var/lib/hitch/certs/service.docker.pem + - SERVICE_IGNORE=true + ports: + - "80:80" + - "8081:8081" + - "8082:8082" + - "8083:8083" + volumes: + - ./hitch:/var/lib/hitch registrator: - image: ehedenst/registrator:x86 + image: hedenstroem/registrator command: -internal -ttl 600 -ttl-refresh 300 -resync 600 -cleanup consul://consul:8500/dc1 links: - consul @@ -28,9 +51,12 @@ services: - /var/run/docker.sock:/tmp/docker.sock hitch: - image: ehedenst/hitch:x86 + image: hedenstroem/hitch + links: + - consul_proxy environment: - SERVICE_402_NAME=acme-challenge + - SERVICE_403_IGNORE=true - SERVICE_443_NAME=hitch - MODE=staging ports: @@ -39,15 +65,38 @@ services: - ./hitch:/var/lib/hitch - ./acme:/var/lib/acme + elasticsearch: + image: elasticsearch:2 + command: elasticsearch -Des.network.host=0.0.0.0 + environment: + - SERVICE_9200_NAME=elasticsearch + - SERVICE_9300_IGNORE=true + logstash: - image: ehedenst/logstash:x86 + image: logstash:2 + command: -f /opt/logstash/config/logstash.conf + environment: + - SERVICE_5000_NAME=logstash-udp + - SERVICE_8080_NAME=logstash-http + links: + - elasticsearch ports: - "5000/udp" + - "8080" volumes: - ./logstash:/opt/logstash/config + kibana: + image: hedenstroem/kibana + environment: + - ELASTICSEARCH_URL=http://elasticsearch:9200 + ports: + - "5601:5601" + links: + - elasticsearch + test-slow: - image: ehedenst/php5:x86 + image: hedenstroem/php5 environment: - SERVICE_NAME=test - SERVICE_TAGS=php5,slow @@ -57,7 +106,7 @@ services: - ./www:/var/www test-medium: - image: ehedenst/php5:x86 + image: hedenstroem/php5 environment: - SERVICE_NAME=test - SERVICE_TAGS=php5,medium @@ -67,7 +116,7 @@ services: - ./www:/var/www test-fast: - image: ehedenst/php5:x86 + image: hedenstroem/php5 environment: - SERVICE_NAME=test - SERVICE_TAGS=php5,fast diff --git a/test/hitch/conf/hitch.conf b/test/hitch/conf/hitch.conf index 42c7914a6d62a1114cbfe08abecbbf2de6b42e5d..b1eae575feab771e7a059fbcd7e55ac182f3227c 100644 --- a/test/hitch/conf/hitch.conf +++ b/test/hitch/conf/hitch.conf @@ -14,7 +14,7 @@ frontend = "[*]:443" # # type: string # syntax: [HOST]:PORT. -backend = "[10.0.2.2]:8081" +backend = "[consul_proxy]:8081" # SSL x509 certificate file. REQUIRED. # List multiple certs to use SNI. Certs are used in the order they diff --git a/test/logstash/logstash.conf b/test/logstash/logstash.conf index 31616602a5befb9099f2ef7a21ae2a2f7deb2020..8e5c8957dc043160696bb0b3c62e7442c2e18213 100644 --- a/test/logstash/logstash.conf +++ b/test/logstash/logstash.conf @@ -4,7 +4,13 @@ input { codec => msgpack type => "consul_proxy" } + http { + port => 8080 + } } output { stdout { codec => rubydebug } + elasticsearch { + hosts => "elasticsearch:9200" + } }