summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lehnardt <jan@apache.org>2018-09-15 09:50:37 +0200
committerJan Lehnardt <jan@apache.org>2018-11-09 14:51:33 +0100
commit67f4da6e0bfbf31f1f72d9d9431f03a5d870d561 (patch)
treeb05d30c61791ec4346706394af95c9f540af1109
parent36e2323044dee3cadf5f766462000d7be89fc4dc (diff)
downloadcouchdb-67f4da6e0bfbf31f1f72d9d9431f03a5d870d561.tar.gz
feat: remove deprecated os_daemons
-rw-r--r--src/couch/src/couch_os_daemons.erl405
-rw-r--r--src/couch/src/couch_secondary_sup.erl1
-rw-r--r--src/couch/test/couchdb_os_daemons_tests.erl259
3 files changed, 0 insertions, 665 deletions
diff --git a/src/couch/src/couch_os_daemons.erl b/src/couch/src/couch_os_daemons.erl
deleted file mode 100644
index 2427d67ab..000000000
--- a/src/couch/src/couch_os_daemons.erl
+++ /dev/null
@@ -1,405 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
--module(couch_os_daemons).
--behaviour(gen_server).
--vsn(1).
--behaviour(config_listener).
-
--export([start_link/0, info/0, info/1]).
-
--export([init/1, terminate/2, code_change/3]).
--export([handle_call/3, handle_cast/2, handle_info/2]).
-
-% config_listener api
--export([handle_config_change/5, handle_config_terminate/3]).
-
--include_lib("couch/include/couch_db.hrl").
-
--record(daemon, {
- port,
- name,
- cmd,
- kill,
- status=running,
- cfg_patterns=[],
- errors=[],
- buf=[]
-}).
-
--define(PORT_OPTIONS, [stream, {line, 1024}, binary, exit_status, hide]).
--define(RELISTEN_DELAY, 5000).
-
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-info() ->
- info([]).
-
-info(Options) ->
- gen_server:call(?MODULE, {daemon_info, Options}).
-
-init(_) ->
- process_flag(trap_exit, true),
- ok = config:listen_for_changes(?MODULE, nil),
- Table = ets:new(?MODULE, [protected, set, {keypos, #daemon.port}]),
- reload_daemons(Table),
- {ok, Table}.
-
-terminate(_Reason, Table) ->
- [stop_port(D) || D <- ets:tab2list(Table)],
- ok.
-
-handle_call({daemon_info, Options}, _From, Table) when is_list(Options) ->
- case lists:member(table, Options) of
- true ->
- {reply, {ok, ets:tab2list(Table)}, Table};
- _ ->
- {reply, {ok, Table}, Table}
- end;
-handle_call(Msg, From, Table) ->
- couch_log:error("Unknown call message to ~p from ~p: ~p",
- [?MODULE, From, Msg]),
- {stop, error, Table}.
-
-handle_cast({config_change, Sect, Key}, Table) ->
- restart_daemons(Table, Sect, Key),
- {noreply, Table};
-handle_cast(stop, Table) ->
- {stop, normal, Table};
-handle_cast(Msg, Table) ->
- couch_log:error("Unknown cast message to ~p: ~p", [?MODULE, Msg]),
- {stop, error, Table}.
-
-handle_info({'EXIT', Port, Reason}, Table) ->
- case ets:lookup(Table, Port) of
- [] ->
- couch_log:info("Port ~p exited after stopping: ~p~n",
- [Port, Reason]);
- [#daemon{status=stopping}] ->
- true = ets:delete(Table, Port);
- [#daemon{name=Name, status=restarting}=D] ->
- couch_log:info("Daemon ~p restarting after config change.", [Name]),
- true = ets:delete(Table, Port),
- {ok, Port2} = start_port(D#daemon.cmd),
- true = ets:insert(Table, D#daemon{
- port=Port2, status=running, kill=undefined, buf=[]
- });
- [#daemon{name=Name, status=halted}] ->
- couch_log:error("Halted daemon process: ~p", [Name]);
- [D] ->
- couch_log:error("Invalid port state at exit: ~p", [D])
- end,
- {noreply, Table};
-handle_info({Port, closed}, Table) ->
- handle_info({Port, {exit_status, closed}}, Table);
-handle_info({Port, {exit_status, Status}}, Table) ->
- case ets:lookup(Table, Port) of
- [] ->
- couch_log:error("Unknown port ~p exiting ~p", [Port, Status]),
- {stop, {error, unknown_port_died, Status}, Table};
- [#daemon{name=Name, status=restarting}=D] ->
- couch_log:info("Daemon ~p restarting after config change.", [Name]),
- true = ets:delete(Table, Port),
- {ok, Port2} = start_port(D#daemon.cmd),
- true = ets:insert(Table, D#daemon{
- port=Port2, status=running, kill=undefined, buf=[]
- }),
- {noreply, Table};
- [#daemon{status=stopping}=D] ->
- % The configuration changed and this daemon is no
- % longer needed.
- couch_log:debug("Port ~p shut down.", [D#daemon.name]),
- true = ets:delete(Table, Port),
- {noreply, Table};
- [D] ->
- % Port died for unknown reason. Check to see if it's
- % died too many times or if we should boot it back up.
- case should_halt([os:timestamp() | D#daemon.errors]) of
- {true, _} ->
- % Halting the process. We won't try and reboot
- % until the configuration changes.
- Fmt = "Daemon ~p halted with exit_status ~p",
- couch_log:error(Fmt, [D#daemon.name, Status]),
- D2 = D#daemon{status=halted, errors=nil, buf=nil},
- true = ets:insert(Table, D2),
- {noreply, Table};
- {false, Errors} ->
- % We're guessing it was a random error, this daemon
- % has behaved so we'll give it another chance.
- Fmt = "Daemon ~p is being rebooted after exit_status ~p",
- couch_log:info(Fmt, [D#daemon.name, Status]),
- true = ets:delete(Table, Port),
- {ok, Port2} = start_port(D#daemon.cmd),
- true = ets:insert(Table, D#daemon{
- port=Port2, status=running, kill=undefined,
- errors=Errors, buf=[]
- }),
- {noreply, Table}
- end;
- _Else ->
- throw(error)
- end;
-handle_info({Port, {data, {noeol, Data}}}, Table) ->
- [#daemon{buf=Buf}=D] = ets:lookup(Table, Port),
- true = ets:insert(Table, D#daemon{buf=[Data | Buf]}),
- {noreply, Table};
-handle_info({Port, {data, {eol, Data}}}, Table) ->
- [#daemon{buf=Buf}=D] = ets:lookup(Table, Port),
- Line = lists:reverse(Buf, Data),
- % The first line echoed back is the kill command
- % for when we go to get rid of the port. Lines after
- % that are considered part of the stdio API.
- case D#daemon.kill of
- undefined ->
- true = ets:insert(Table, D#daemon{kill=?b2l(Line), buf=[]});
- _Else ->
- D2 = case (catch ?JSON_DECODE(Line)) of
- {invalid_json, Rejected} ->
- couch_log:error("Ignoring OS daemon request: ~p",
- [Rejected]),
- D;
- JSON ->
- {ok, D3} = handle_port_message(D, JSON),
- D3
- end,
- true = ets:insert(Table, D2#daemon{buf=[]})
- end,
- {noreply, Table};
-handle_info({Port, Error}, Table) ->
- couch_log:error("Unexpectd message from port ~p: ~p", [Port, Error]),
- stop_port(Port),
- [D] = ets:lookup(Table, Port),
- true = ets:insert(Table, D#daemon{status=restarting, buf=nil}),
- {noreply, Table};
-handle_info(restart_config_listener, State) ->
- ok = config:listen_for_changes(?MODULE, nil),
- {noreply, State};
-handle_info(Msg, Table) ->
- couch_log:error("Unexpected info message to ~p: ~p", [?MODULE, Msg]),
- {stop, error, Table}.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-
-handle_config_change(Section, Key, _, _, _) ->
- gen_server:cast(?MODULE, {config_change, Section, Key}),
- {ok, nil}.
-
-handle_config_terminate(_, stop, _) ->
- ok;
-handle_config_terminate(_Server, _Reason, _State) ->
- erlang:send_after(?RELISTEN_DELAY, whereis(?MODULE), restart_config_listener).
-
-
-% Internal API
-
-%
-% Port management helpers
-%
-
-start_port(Command) ->
- start_port(Command, []).
-
-start_port(Command, EnvPairs) ->
- PrivDir = couch_util:priv_dir(),
- Spawnkiller = "\"" ++ filename:join(PrivDir, "couchspawnkillable") ++ "\"",
- Opts = case lists:keytake(env, 1, ?PORT_OPTIONS) of
- false ->
- ?PORT_OPTIONS ++ [ {env,EnvPairs} ];
- {value, {env,OldPairs}, SubOpts} ->
- AllPairs = lists:keymerge(1, EnvPairs, OldPairs),
- SubOpts ++ [ {env,AllPairs} ]
- end,
- Port = open_port({spawn, Spawnkiller ++ " " ++ Command}, Opts),
- {ok, Port}.
-
-
-stop_port(#daemon{port=Port, kill=undefined}=D) ->
- couch_log:error("Stopping daemon without a kill command: ~p",
- [D#daemon.name]),
- catch port_close(Port);
-stop_port(#daemon{port=Port}=D) ->
- couch_log:debug("Stopping daemon: ~p", [D#daemon.name]),
- os:cmd(D#daemon.kill),
- catch port_close(Port).
-
-
-handle_port_message(#daemon{port=Port}=Daemon, [<<"get">>, Section]) ->
- KVs = config:get(Section),
- Data = lists:map(fun({K, V}) -> {?l2b(K), ?l2b(V)} end, KVs),
- Json = iolist_to_binary(?JSON_ENCODE({Data})),
- port_command(Port, <<Json/binary, "\n">>),
- {ok, Daemon};
-handle_port_message(#daemon{port=Port}=Daemon, [<<"get">>, Section, Key]) ->
- Value = case config:get(Section, Key, undefined) of
- undefined -> null;
- String -> ?l2b(String)
- end,
- Json = iolist_to_binary(?JSON_ENCODE(Value)),
- port_command(Port, <<Json/binary, "\n">>),
- {ok, Daemon};
-handle_port_message(Daemon, [<<"register">>, Sec]) when is_binary(Sec) ->
- Patterns = lists:usort(Daemon#daemon.cfg_patterns ++ [{?b2l(Sec)}]),
- {ok, Daemon#daemon{cfg_patterns=Patterns}};
-handle_port_message(Daemon, [<<"register">>, Sec, Key])
- when is_binary(Sec) andalso is_binary(Key) ->
- Pattern = {?b2l(Sec), ?b2l(Key)},
- Patterns = lists:usort(Daemon#daemon.cfg_patterns ++ [Pattern]),
- {ok, Daemon#daemon{cfg_patterns=Patterns}};
-handle_port_message(#daemon{name=Name}=Daemon, [<<"log">>, Msg]) ->
- handle_log_message(Name, Msg, <<"info">>),
- {ok, Daemon};
-handle_port_message(#daemon{name=Name}=Daemon, [<<"log">>, Msg, {Opts}]) ->
- Level = couch_util:get_value(<<"level">>, Opts, <<"info">>),
- handle_log_message(Name, Msg, Level),
- {ok, Daemon};
-handle_port_message(#daemon{name=Name}=Daemon, Else) ->
- couch_log:error("Daemon ~p made invalid request: ~p", [Name, Else]),
- {ok, Daemon}.
-
-
-handle_log_message(Name, Msg, _Level) when not is_binary(Msg) ->
- couch_log:error("Invalid log message from daemon ~p: ~p", [Name, Msg]);
-handle_log_message(Name, Msg, <<"debug">>) ->
- couch_log:debug("Daemon ~p :: ~s", [Name, ?b2l(Msg)]);
-handle_log_message(Name, Msg, <<"info">>) ->
- couch_log:info("Daemon ~p :: ~s", [Name, ?b2l(Msg)]);
-handle_log_message(Name, Msg, <<"error">>) ->
- couch_log:error("Daemon: ~p :: ~s", [Name, ?b2l(Msg)]);
-handle_log_message(Name, Msg, Level) ->
- couch_log:error("Invalid log level from daemon: ~p", [Level]),
- couch_log:info("Daemon: ~p :: ~s", [Name, ?b2l(Msg)]).
-
-%
-% Daemon management helpers
-%
-
-get_daemons() ->
- % map all COUCHDB_OS_DAEMON_* vars out of os:getenv
- lists:filtermap(fun(Var) ->
- case string:split(Var, "=") of
- [VarName, Cmd] ->
- case string:find(VarName, "COUCHDB_OS_DAEMON_") of
- nomatch ->
- false;
- "COUCHDB_OS_DAEMON_" ++ Name ->
- {true, {string:lowercase(Name), Cmd}}
- end;
- _Else -> false
- end
- end, os:getenv()).
-
-reload_daemons(Table) ->
- % List of daemons we want to have running.
- Configured = lists:sort(get_daemons()),
-
- % Remove records for daemons that were halted.
- MSpecHalted = #daemon{name='$1', cmd='$2', status=halted, _='_'},
- Halted = lists:sort([{N, C} || [N, C] <- ets:match(Table, MSpecHalted)]),
- ok = stop_os_daemons(Table, find_to_stop(Configured, Halted, [])),
-
- % Stop daemons that are running
- % Start newly configured daemons
- MSpecRunning = #daemon{name='$1', cmd='$2', status=running, _='_'},
- Running = lists:sort([{N, C} || [N, C] <- ets:match(Table, MSpecRunning)]),
- ok = stop_os_daemons(Table, find_to_stop(Configured, Running, [])),
- ok = boot_os_daemons(Table, find_to_boot(Configured, Running, [])),
- ok.
-
-
-restart_daemons(Table, Sect, Key) ->
- restart_daemons(Table, Sect, Key, ets:first(Table)).
-
-restart_daemons(_, _, _, '$end_of_table') ->
- ok;
-restart_daemons(Table, Sect, Key, Port) ->
- [D] = ets:lookup(Table, Port),
- HasSect = lists:member({Sect}, D#daemon.cfg_patterns),
- HasKey = lists:member({Sect, Key}, D#daemon.cfg_patterns),
- case HasSect or HasKey of
- true ->
- stop_port(D),
- D2 = D#daemon{status=restarting, buf=nil},
- true = ets:insert(Table, D2);
- _ ->
- ok
- end,
- restart_daemons(Table, Sect, Key, ets:next(Table, Port)).
-
-
-stop_os_daemons(_Table, []) ->
- ok;
-stop_os_daemons(Table, [{Name, Cmd} | Rest]) ->
- [[Port]] = ets:match(Table, #daemon{port='$1', name=Name, cmd=Cmd, _='_'}),
- [D] = ets:lookup(Table, Port),
- case D#daemon.status of
- halted ->
- ets:delete(Table, Port);
- _ ->
- stop_port(D),
- D2 = D#daemon{status=stopping, errors=nil, buf=nil},
- true = ets:insert(Table, D2)
- end,
- stop_os_daemons(Table, Rest).
-
-boot_os_daemons(_Table, []) ->
- ok;
-boot_os_daemons(Table, [{Name, Cmd} | Rest]) ->
- {ok, Port} = start_port(Cmd),
- true = ets:insert(Table, #daemon{port=Port, name=Name, cmd=Cmd}),
- boot_os_daemons(Table, Rest).
-
-% Elements unique to the configured set need to be booted.
-find_to_boot([], _Rest, Acc) ->
- % Nothing else configured.
- Acc;
-find_to_boot([D | R1], [D | R2], Acc) ->
- % Elements are equal, daemon already running.
- find_to_boot(R1, R2, Acc);
-find_to_boot([D1 | R1], [D2 | _]=A2, Acc) when D1 < D2 ->
- find_to_boot(R1, A2, [D1 | Acc]);
-find_to_boot(A1, [_ | R2], Acc) ->
- find_to_boot(A1, R2, Acc);
-find_to_boot(Rest, [], Acc) ->
- % No more candidates for already running. Boot all.
- Rest ++ Acc.
-
-% Elements unique to the running set need to be killed.
-find_to_stop([], Rest, Acc) ->
- % The rest haven't been found, so they must all
- % be ready to die.
- Rest ++ Acc;
-find_to_stop([D | R1], [D | R2], Acc) ->
- % Elements are equal, daemon already running.
- find_to_stop(R1, R2, Acc);
-find_to_stop([D1 | R1], [D2 | _]=A2, Acc) when D1 < D2 ->
- find_to_stop(R1, A2, Acc);
-find_to_stop(A1, [D2 | R2], Acc) ->
- find_to_stop(A1, R2, [D2 | Acc]);
-find_to_stop(_, [], Acc) ->
- % No more running daemons to worry about.
- Acc.
-
-should_halt(Errors) ->
- RetryTimeCfg = config:get("os_daemon_settings", "retry_time", "5"),
- RetryTime = list_to_integer(RetryTimeCfg),
-
- Now = os:timestamp(),
- RecentErrors = lists:filter(fun(Time) ->
- timer:now_diff(Now, Time) =< RetryTime * 1000000
- end, Errors),
-
- RetryCfg = config:get("os_daemon_settings", "max_retries", "3"),
- Retries = list_to_integer(RetryCfg),
-
- {length(RecentErrors) >= Retries, RecentErrors}.
diff --git a/src/couch/src/couch_secondary_sup.erl b/src/couch/src/couch_secondary_sup.erl
index cfbce6a9a..d78b5b15b 100644
--- a/src/couch/src/couch_secondary_sup.erl
+++ b/src/couch/src/couch_secondary_sup.erl
@@ -33,7 +33,6 @@ init([]) ->
{"httpd", "{couch_httpd, start_link, []}"},
{"uuids", "{couch_uuids, start, []}"},
{"auth_cache", "{couch_auth_cache, start_link, []}"},
- {"os_daemons", "{couch_os_daemons, start_link, []}"},
{"compaction_daemon", "{couch_compaction_daemon, start_link, []}"}
],
diff --git a/src/couch/test/couchdb_os_daemons_tests.erl b/src/couch/test/couchdb_os_daemons_tests.erl
deleted file mode 100644
index 1728314bb..000000000
--- a/src/couch/test/couchdb_os_daemons_tests.erl
+++ /dev/null
@@ -1,259 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couchdb_os_daemons_tests).
-
-%% tests are UNIX-specific, will not function under Windows
--ifdef(WINDOWS).
--undef(TEST).
--define(NOTEST, 1).
--endif.
-
--include_lib("couch/include/couch_eunit.hrl").
-
-%% keep in sync with couchdb/couch_os_daemons.erl
--record(daemon, {
- port,
- name,
- cmd,
- kill,
- status=running,
- cfg_patterns=[],
- errors=[],
- buf=[]
-}).
-
--define(DAEMON_CONFIGER, "os_daemon_configer.escript").
--define(DAEMON_LOOPER, "os_daemon_looper.escript").
--define(DAEMON_BAD_PERM, "os_daemon_bad_perm.sh").
--define(DAEMON_CAN_REBOOT, "os_daemon_can_reboot.sh").
--define(DAEMON_DIE_ON_BOOT, "os_daemon_die_on_boot.sh").
--define(DAEMON_DIE_QUICKLY, "os_daemon_die_quickly.sh").
--define(TRIES, 40).
--define(TRY_DELAY_MS, 100).
--define(TIMEOUT, 10000).
--define(CONFIG_TIMEOUT, 1000).
-
-
-setup(DName) ->
- Ctx = test_util:start(?MODULE, [couch_log], [{dont_mock, [config]}]),
- {ok, OsDPid} = couch_os_daemons:start_link(),
- config:set("os_daemons", DName,
- filename:join([?FIXTURESDIR, DName]), false),
- % Set configuration option to be used by configuration_reader_test_
- % This will be used in os_daemon_configer.escript:test_get_cfg2
- config:set("uuids", "algorithm","sequential", false),
- config:set("os_daemon_settings", "max_retries", "2", false),
- ensure_n_daemons_are_alive(1),
- {Ctx, OsDPid}.
-
-teardown(_, {Ctx, OsDPid}) ->
- try
- test_util:stop_sync_throw(OsDPid, fun() ->
- exit(OsDPid, shutdown)
- end, {timeout, os_daemon_stop}, ?TIMEOUT)
- catch
- {timeout, os_daemon_stop} ->
- Msg = "~nWARNING: OS daemons test stop ~p msec timeout exceeded~n",
- io:format(standard_error, Msg, [?TIMEOUT]),
- exit(OsDPid, kill)
- end,
- test_util:stop(Ctx).
-
-
-os_daemons_test_() ->
- {
- "OS Daemons tests",
- {
- foreachx,
- fun setup/1, fun teardown/2,
- [{?DAEMON_LOOPER, Fun} || Fun <- [
- fun should_check_daemon/2,
- fun should_check_daemon_table_form/2,
- fun should_clean_tables_on_daemon_remove/2,
- fun should_spawn_multiple_daemons/2,
- fun should_keep_alive_one_daemon_on_killing_other/2
- ]]
- }
- }.
-
-configuration_reader_test_() ->
- {
- "OS Daemon requests CouchDB configuration",
- {
- foreachx,
- fun setup/1, fun teardown/2,
- [{?DAEMON_CONFIGER,
- fun should_read_write_config_settings_by_daemon/2}]
-
- }
- }.
-
-error_test_() ->
- {
- "OS Daemon process error tests",
- {
- foreachx,
- fun setup/1, fun teardown/2,
- [{?DAEMON_BAD_PERM, fun should_fail_due_to_lack_of_permissions/2},
- {?DAEMON_DIE_ON_BOOT, fun should_die_on_boot/2},
- {?DAEMON_DIE_QUICKLY, fun should_die_quickly/2},
- {?DAEMON_CAN_REBOOT, fun should_not_being_halted/2}]
- }
- }.
-
-
-should_check_daemon(DName, _) ->
- ?_test(begin
- {ok, [D]} = couch_os_daemons:info([table]),
- check_daemon(D, DName)
- end).
-
-should_check_daemon_table_form(DName, _) ->
- ?_test(begin
- {ok, Tab} = couch_os_daemons:info(),
- [D] = ets:tab2list(Tab),
- check_daemon(D, DName)
- end).
-
-should_clean_tables_on_daemon_remove(DName, _) ->
- ?_test(begin
- config:delete("os_daemons", DName, false),
- {ok, Tab2} = couch_os_daemons:info(),
- ?_assertEqual([], ets:tab2list(Tab2))
- end).
-
-should_spawn_multiple_daemons(DName, _) ->
- ?_test(begin
- config:set("os_daemons", "bar",
- filename:join([?FIXTURESDIR, DName]), false),
- config:set("os_daemons", "baz",
- filename:join([?FIXTURESDIR, DName]), false),
- ensure_n_daemons_are_alive(3), % DName, "bar" and "baz"
- {ok, Daemons} = couch_os_daemons:info([table]),
- lists:foreach(fun(D) ->
- check_daemon(D)
- end, Daemons),
- {ok, Tab} = couch_os_daemons:info(),
- lists:foreach(fun(D) ->
- check_daemon(D)
- end, ets:tab2list(Tab))
- end).
-
-should_keep_alive_one_daemon_on_killing_other(DName, _) ->
- ?_test(begin
- config:set("os_daemons", "bar",
- filename:join([?FIXTURESDIR, DName]), false),
- ensure_n_daemons_are_alive(2), % DName and "bar"
- {ok, Daemons} = couch_os_daemons:info([table]),
- lists:foreach(fun(D) ->
- check_daemon(D)
- end, Daemons),
-
- config:delete("os_daemons", "bar", false),
- ensure_n_daemons_are_alive(1), % Dname only, "bar" should be dead
- {ok, [D2]} = couch_os_daemons:info([table]),
- check_daemon(D2, DName),
-
- {ok, Tab} = couch_os_daemons:info(),
- [T] = ets:tab2list(Tab),
- check_daemon(T, DName)
- end).
-
-should_read_write_config_settings_by_daemon(DName, _) ->
- ?_test(begin
- % have to wait till daemon run all his tests
- % see daemon's script for more info
- timer:sleep(?CONFIG_TIMEOUT),
- {ok, [D]} = couch_os_daemons:info([table]),
- check_daemon(D, DName)
- end).
-
-should_fail_due_to_lack_of_permissions(DName, _) ->
- ?_test(should_halts(DName, 1000)).
-
-should_die_on_boot(DName, _) ->
- ?_test(should_halts(DName, 2000)).
-
-should_die_quickly(DName, _) ->
- ?_test(should_halts(DName, 4000)).
-
-should_not_being_halted(DName, _) ->
- ?_test(begin
- timer:sleep(1000),
- {ok, [D1]} = couch_os_daemons:info([table]),
- check_daemon(D1, DName, 0),
-
- % Should reboot every two seconds. We're at 1s, so wait
- % until 3s to be in the middle of the next invocation's
- % life span.
-
- timer:sleep(2000),
- {ok, [D2]} = couch_os_daemons:info([table]),
- check_daemon(D2, DName, 1),
-
- % If the kill command changed, that means we rebooted the process.
- ?assertNotEqual(D1#daemon.kill, D2#daemon.kill)
- end).
-
-should_halts(DName, Time) ->
- timer:sleep(Time),
- {ok, [D]} = couch_os_daemons:info([table]),
- check_dead(D, DName),
- config:delete("os_daemons", DName, false).
-
-check_daemon(D) ->
- check_daemon(D, D#daemon.name).
-
-check_daemon(D, Name) ->
- check_daemon(D, Name, 0).
-
-check_daemon(D, Name, Errs) ->
- ?assert(is_port(D#daemon.port)),
- ?assertEqual(Name, D#daemon.name),
- ?assertNotEqual(undefined, D#daemon.kill),
- ?assertEqual(running, D#daemon.status),
- ?assertEqual(Errs, length(D#daemon.errors)),
- ?assertEqual([], D#daemon.buf).
-
-check_dead(D, Name) ->
- ?assert(is_port(D#daemon.port)),
- ?assertEqual(Name, D#daemon.name),
- ?assertNotEqual(undefined, D#daemon.kill),
- ?assertEqual(halted, D#daemon.status),
- ?assertEqual(nil, D#daemon.errors),
- ?assertEqual(nil, D#daemon.buf).
-
-daemons() ->
- {ok, Daemons} = couch_os_daemons:info([table]),
- Daemons.
-
-ensure_n_daemons_are_alive(NumDaemons) ->
- retry(fun() -> length(daemons()) == NumDaemons end, "spawning"),
- retry(fun() ->
- lists:all(fun(D) -> D#daemon.kill =/= undefined end, daemons())
- end, "waiting for kill flag").
-
-retry(Pred, FailReason) ->
- retry(Pred, ?TRIES, FailReason).
-
-retry(_Pred, 0, FailReason) ->
- erlang:error({assertion_failed,[{module, ?MODULE}, {line, ?LINE},
- {reason, "Timed out: " ++ FailReason}]});
-retry(Pred, N, FailReason) ->
- case Pred() of
- true ->
- ok;
- false ->
- timer:sleep(?TRY_DELAY_MS),
- retry(Pred, N - 1, FailReason)
- end.