anchor.diff:  
   leave() ->
       error_logger:info_msg("ns_cluster: we've been asked to leave the cluster.~n"),
  -    gen_server:cast(?MODULE, leave).
  +    gen_fsm:send_event(?MODULE, leave).
   
  
positive.code: 
  -module(ns_cluster).
  
  -behaviour(gen_server).
  
  -export([start_link/0, init/1, handle_cast/2, handle_call/3, handle_info/2,
           terminate/2, code_change/3]).
  
  %% API
  -export([join/2, leave/1, leave/0]).
  
  -record(state, {child, action}).
  
  %% gen_server handlers
  start_link() ->
      gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  
  init([]) ->
      process_flag(trap_exit, true),
      {ok, Pid} = ns_server_sup:start_link(),
      {ok, #state{child=Pid, action=undefined}}.
  
  handle_call(Request, _From, State) ->
      {reply, {unhandled, ?MODULE, Request}, State}.
  
  handle_cast({join, RemoteNode, NewCookie}, State = #state{child=Pid, action=undefined}) ->
      ns_log:log(?MODULE, 0002, "Node ~p is joining cluster.", [RemoteNode]),
      ns_config:set(otp, [{cookie, NewCookie}]),
      ns_config:clear([directory, otp]),
      true = exit(Pid, shutdown), % Pull the rug out from under the app
      {noreply, State#state{action={join, RemoteNode, NewCookie}}};
  
  handle_cast(leave, State = #state{child=Pid, action=undefined}) ->
      ns_log:log(?MODULE, 0001, "Node ~p is leaving cluster.", [node()]),
      NewCookie = ns_node_disco:cookie_gen(),
      ns_config:set(nodes_wanted, [node()]),
      ns_config:set(otp, [{cookie, NewCookie}]),
      true = exit(Pid, shutdown),
      {noreply, State#state{action=leave}};
  
  handle_cast(Msg, State) ->
      error_logger:info_msg("ns_cluster: got unexpected cast ~p in state ~p~n",
                                [Msg, State]),
      {noreply, State}.
  
  handle_info({'EXIT', Pid, shutdown},
              #state{child=Pid, action={join, RemoteNode, NewCookie}}) ->
      error_logger:info_msg("ns_cluster: joining cluster. Child has exited.~n"),
      timer:sleep(1000), % Sleep for a second to let things settle
      true = erlang:set_cookie(node(), NewCookie),
  
negative.code:         Nodes -> rpc:cast(RemoteNode, ns_config, set,
                            [nodes_wanted, [node() | Nodes]])
      end,
      {ok, running, State} = init([]),
      {next_state, running, State}.
  
  leaving({exit, _Pid}, _LeaveData) ->
      error_logger:info_msg("ns_cluster: leaving cluster~n"),
      timer:sleep(1000),
      lists:foreach(fun erlang:disconnect_node/1, nodes()),
      {ok, running, State} = init([]),
      {next_state, running, State};
  
  leaving(leave, LeaveData) ->
      %% If we are told to leave in the leaving state, continue leaving.
      {next_state, leaving, LeaveData}.
  
  %%
  %% Miscellaneous gen_fsm callbacks.
  %%
  
  handle_info({'EXIT', Pid, shutdown}, CurrentState, CurrentData) ->
      ?MODULE:CurrentState({exit, Pid}, CurrentData).
  
  handle_event(Event, State, _StateData) ->
      exit({unhandled_event, Event, State}).
  
  handle_sync_event(Event, _From, State, _StateData) ->
      exit({unhandled_event, Event, State}).
  
  code_change(_OldVsn, State, StateData, _Extra) ->
      {ok, State, StateData}.
  
  terminate(_Reason, _StateName, _StateData) -> ok.
  
  %% API
  join(RemoteNode, NewCookie) ->
      gen_fsm:send_event(?MODULE, {join, RemoteNode, NewCookie}).
  
  % Should be invoked on a node that remains in the cluster,
  % where the leaving RemoteNode is passed in as an argument.
  