anchor.diff:      case proplists:get_value(<<"couchApiBase">>, NodeKV) of
           undefined ->
               ?log_debug("skipping node without couchApiBase: ~p", [NodeKV]),
  -            [];
  +            {ok, []};
           _ ->
  -            RV = extract_node_props([{<<"hostname">>, fun extract_string/2},
  -                                     {<<"couchApiBase">>, fun extract_string/2},
  -                                     {<<"ports">>, fun extract_object/2}],
  -                                    Ctx, {struct, NodeKV}),
  -            case RV of
  -                [Props] ->
  -                    case proplists:get_value(<<"couchApiBaseHTTPS">>, NodeKV) of
  -                        undefined ->
  -                            RV;
  -                        _ ->
  -                            case extract_node_props([{<<"couchApiBaseHTTPS">>, fun extract_string/2}],
  -                                                    Ctx, {struct, NodeKV}) of
  -                                [] ->
  -                                    RV;
  -                                [Props2] ->
  -                                    ?log_debug("extracted couchApiBaseHTTPS"),
  -                                    [Props2 ++ Props]
  -                            end
  -                    end;
  -                [] ->
  -                    RV
  -            end
  +            extract_node_props([{<<"hostname">>, fun extract_string/2},
  +                                {<<"couchApiBase">>, fun extract_string/2},
  +                                {<<"ports">>, fun extract_object/2},
  +                                {<<"couchApiBaseHTTPS">>, {optional, fun extract_string/2}}],
  +                               Ctx, {struct, NodeKV})
       end;
   maybe_extract_important_buckets_props(BadNodeStruct, Ctx) ->
       extract_node_props([], Ctx, BadNodeStruct).
  +
  +maybe_extract_important_buckets_props_basic_test() ->
  +    Ctx = <<"s">>,
  +    {ok, []} = maybe_extract_important_buckets_props({struct, []}, Ctx),
  +    KV0 = [{<<"hostname">>, <<"h">>},
  +           {<<"couchApiBase">>, <<"ab">>},
  +           {<<"ports">>, {struct, []}}],
  +    KV1 = KV0 ++ [{<<"couchApiBaseHTTPS">>, <<"sab">>}],
  +    Ex0 = lists:keyreplace(<<"ports">>, 1, KV0, {<<"ports">>, []}),
  +    Ex1 = lists:keyreplace(<<"ports">>, 1, KV1, {<<"ports">>, []}),
  +    {ok, [Ex0]} = maybe_extract_important_buckets_props({struct, KV0}, Ctx),
  +    {ok, [Ex1]} = maybe_extract_important_buckets_props({struct, KV1}, Ctx),
  +    ?assertMatch({error, _, _},
  +                 maybe_extract_important_buckets_props(
  +                   {struct, lists:keydelete(<<"hostname">>, 1, KV0)}, Ctx)).
   
   
   remote_bucket_with_bucket(BucketName, BucketObject, OrigRemoteCluster,
  
positive.code:       PoolDetails, <<"default pool details">>,
        [{<<"hostname">>, fun extract_string/2},
         {<<"ports">>, fun extract_object/2},
         {<<"version">>, fun extract_version/2}],
        fun (PoolNodeProps) ->
                with_buckets(
                  PoolDetails, JsonGet,
  
negative.code:     expect_nested_array(
        <<"nodes">>, Object, Context,
        fun (NodeList) ->
                Nodes0 = [extract_node_props(Props, Context, Node)
                          || Node <- NodeList],
                BadNodes = [E || E <- Nodes0,
                                 case E of
                                     {ok, _} -> false;
                                     _ -> true
                                 end],
                GoodNodes = lists:append([N || {ok, N} <- Nodes0]),
                case BadNodes of
                    [] ->
                        K(GoodNodes);
                    _ ->
                        Msg = io_lib:format("(~s) unable to extract some nodes: ~p",
                                            [Context, BadNodes]),
                        {error, {bad_value, <<"nodes">>}, iolist_to_binary(Msg)}
                end
        end).
  
  with_nodes_basic_test() ->
      Kf = fun (Value) -> {ok, {v, Value}} end,
  
      ?assertMatch({error, {missing_field, _}, _}, with_nodes([], <<"s">>, [], Kf)),
  
      {ok, {v, []}} = with_nodes([{<<"nodes">>, []}], <<"s">>, [], Kf),
  
      UnreachFn = fun () -> erlang:error(a) end,
      {ok, {v, []}} = with_nodes([{<<"nodes">>, []}], <<"s">>, UnreachFn , Kf),
  
      N1 = [{<<"hostname">>, <<"asd.asd">>},
            {<<"opt">>, 1}],
      N2 = [{<<"hostname">>, <<"basd.asd">>}],
      Nodes = [{<<"nodes">>, [{struct, N1}, {struct, N2}]}],
  
      RV1 = with_nodes(Nodes, <<"s">>, [{<<"unknown">>, UnreachFn}], Kf),
      ?assertMatch({error, _, _}, RV1),
  
      RV2 = with_nodes(Nodes,
                       <<"s">>, [{<<"hostname">>, fun extract_string/2},
                                 {<<"opt">>, {optional, fun extract_number/2}}],
                       Kf),
      {ok, {v, [[{<<"hostname">>, <<"asd.asd">>},
                 {<<"opt">>, 1}],
                [{<<"hostname">>, <<"basd.asd">>}]]}} =
          RV2,
  
      {ok, {v, []}} = with_nodes(Nodes, <<"s">>,
                                 fun (_NI, _Ctx) -> {ok, []} end,
                                 Kf),
  
      {ok, {v, [foo, bar, foo, bar]}} =
          with_nodes(Nodes, <<"s">>,
                     fun (_NI, _Ctx) -> {ok, [foo, bar]} end,
                     Kf).
  
  
  extract_node_props_loop([{Prop, Extract0} | RestProps], Context, Node) ->
      {Optional, Extract} = case Extract0 of
                                {optional, XE} ->
                                    {true, XE};
                                _ ->
                                    {false, Extract0}
                            end,
      case proplists:get_value(Prop, Node) of
          undefined ->
              case Optional of
                  true ->
                      ?xdcr_debug("Missing optional `~s` field in node info:~n~p",
                                  [Prop, Node]),
                      extract_node_props_loop(RestProps, Context, Node);
                  false ->
                      {error, {missing_field, Prop}, <<"">>}
              end;
          Value ->
              case Extract(Value, Context) of
                  {ok, FinalValue} ->
                      RestExtracted0 = extract_node_props_loop(RestProps, Context, Node),
                      case RestExtracted0 of
                          {ok, RestExtracted} ->
                              {ok, [{Prop, FinalValue} | RestExtracted]};
                          Err ->
                              Err
                      end;
                  {bad_value, Msg} ->
                      case Optional of
                          true ->
                              ?log_warning("~s:~n~p", [Msg, Value]),
                              extract_node_props_loop(RestProps, Context, Node);
                          false ->
                              {error, {bad_value, Prop}, Msg}
                      end
              end
      end;
  extract_node_props_loop([], _Context, _Node) ->
      {ok, []}.
  
  extract_node_props(PropsExtractor, Context, NodeStruct) when is_function(PropsExtractor) ->
      PropsExtractor(NodeStruct, Context);
  extract_node_props(Props, Context, {struct, Node}) ->
      case extract_node_props_loop(Props, Context, Node) of
          {ok, PList} ->
              {ok, [PList]};
          Err -> Err
      end;
  extract_node_props(_Props, Context, Node) ->
      Msg0 = io_lib:format("(~s) got invalid node info value:~n~p", [Context, Node]),
      Msg = iolist_to_binary(Msg0),
      ?log_warning("~s", [Msg]),
      {error, bad_value, Msg}.
  
  props_to_remote_node(Props) ->
      Hostname = proplists:get_value(<<"hostname">>, Props),
  