xref: /5.5.2/ns_server/src/ns_ports_setup.erl (revision 1ce4d86f)
1-module(ns_ports_setup).
2
3-include("ns_common.hrl").
4
5-export([start/0, setup_body_tramp/0,
6         restart_port_by_name/1, restart_moxi/0, restart_memcached/0,
7         restart_xdcr_proxy/0, sync/0, create_erl_node_spec/4,
8         create_goxdcr_upgrade_spec/1, shutdown_ports/0]).
9
10-export([run_cbsasladm/1]).
11
12start() ->
13    proc_lib:start_link(?MODULE, setup_body_tramp, []).
14
15sync() ->
16    gen_server:call(?MODULE, sync, infinity).
17
18shutdown_ports() ->
19    gen_server:call(?MODULE, shutdown_ports, infinity).
20
21%% ns_config announces full list as well which we don't need
22is_useless_event(List) when is_list(List) ->
23    true;
24%% config changes for other nodes is quite obviously irrelevant
25is_useless_event({{node, N, _}, _}) when N =/= node() ->
26    true;
27is_useless_event(_) ->
28    false.
29
30setup_body_tramp() ->
31    misc:delaying_crash(1000, fun setup_body/0).
32
33setup_body() ->
34    Self = self(),
35    erlang:register(?MODULE, Self),
36    proc_lib:init_ack({ok, Self}),
37    ns_pubsub:subscribe_link(ns_config_events,
38                             fun (Event) ->
39                                     case is_useless_event(Event) of
40                                         false ->
41                                             Self ! check_children_update;
42                                         _ ->
43                                             []
44                                     end
45                             end),
46    ns_pubsub:subscribe_link(user_storage_events,
47                             fun (_) ->
48                                     Self ! check_children_update
49                             end),
50    Children = dynamic_children(normal),
51    set_children_and_loop(Children, undefined, normal).
52
53%% rpc:called (2.0.2+) after any bucket is deleted
54restart_moxi() ->
55    {ok, _} = restart_port_by_name(moxi),
56    ok.
57
58restart_memcached() ->
59    {ok, _} = restart_port_by_name(memcached),
60    ok.
61
62restart_xdcr_proxy() ->
63    case restart_port_by_name(xdcr_proxy) of
64        {ok, _} ->
65            ok;
66        Error ->
67            Error
68    end.
69
70restart_port_by_name(Name) ->
71    ns_ports_manager:restart_port_by_name(ns_server:get_babysitter_node(), Name).
72
73set_children(Children, Sup) ->
74    Pid = ns_ports_manager:set_dynamic_children(ns_server:get_babysitter_node(), Children),
75    case Sup of
76        undefined ->
77            {is_pid, true, Pid} = {is_pid, erlang:is_pid(Pid), Pid},
78            ?log_debug("Monitor ns_child_ports_sup ~p", [Pid]),
79            remote_monitors:monitor(Pid);
80        Pid ->
81            ok;
82        _ ->
83            ?log_debug("ns_child_ports_sup was restarted on babysitter node. Exit. Old pid = ~p, new pid = ~p",
84                       [Sup, Pid]),
85            erlang:error(child_ports_sup_died)
86    end,
87    Pid.
88
89set_children_and_loop(Children, Sup, Status) ->
90    NewSup = set_children(Children, Sup),
91    children_loop(Children, NewSup, Status).
92
93children_loop(Children, Sup, Status) ->
94    proc_lib:hibernate(erlang, apply, [fun children_loop_continue/3, [Children, Sup, Status]]).
95
96children_loop_continue(Children, Sup, Status) ->
97    receive
98        {'$gen_call', From, shutdown_ports} ->
99            ?log_debug("Send shutdown to all go ports"),
100            NewStatus = shutdown,
101            NewChildren = dynamic_children(NewStatus),
102            NewSup = set_children(NewChildren, Sup),
103            gen_server:reply(From, ok),
104            children_loop(NewChildren, NewSup, NewStatus);
105        check_children_update ->
106            do_children_loop_continue(Children, Sup, Status);
107        {'$gen_call', From, sync} ->
108            gen_server:reply(From, ok),
109            children_loop(Children, Sup, Status);
110        {remote_monitor_down, Sup, unpaused} ->
111            ?log_debug("Remote monitor ~p was unpaused after node name change. Restart loop.", [Sup]),
112            set_children_and_loop(dynamic_children(Status), undefined, Status);
113        {remote_monitor_down, Sup, Reason} ->
114            ?log_debug("ns_child_ports_sup ~p died on babysitter node with ~p. Restart.", [Sup, Reason]),
115            erlang:error({child_ports_sup_died, Sup, Reason});
116        X ->
117            erlang:error({unexpected_message, X})
118    after 0 ->
119            erlang:error(expected_some_message)
120    end.
121
122do_children_loop_continue(Children, Sup, Status) ->
123    %% this sets bound on frequency of checking of port_servers
124    %% configuration updates. NOTE: this thing also depends on other
125    %% config variables. Particularly moxi's environment variables
126    %% need admin credentials. So we're forced to react on any config
127    %% change
128    timer:sleep(50),
129    misc:flush(check_children_update),
130    case dynamic_children(Status) of
131        Children ->
132            children_loop(Children, Sup, Status);
133        NewChildren ->
134            set_children_and_loop(NewChildren, Sup, Status)
135    end.
136
137maybe_create_ssl_proxy_spec(Config) ->
138    UpstreamPort = ns_config:search(Config, {node, node(), ssl_proxy_upstream_port}, undefined),
139    DownstreamPort = ns_config:search(Config, {node, node(), ssl_proxy_downstream_port}, undefined),
140    LocalMemcachedPort = ns_config:search_node_prop(node(), Config, memcached, port),
141    case UpstreamPort =/= undefined andalso DownstreamPort =/= undefined of
142        true ->
143            [create_ssl_proxy_spec(UpstreamPort, DownstreamPort, LocalMemcachedPort)];
144        _ ->
145            []
146    end.
147
148create_ssl_proxy_spec(UpstreamPort, DownstreamPort, LocalMemcachedPort) ->
149    Path = ns_ssl_services_setup:ssl_cert_key_path(),
150    CACertPath = ns_ssl_services_setup:ssl_cacert_key_path(),
151
152    Args = [{upstream_port, UpstreamPort},
153            {downstream_port, DownstreamPort},
154            {local_memcached_port, LocalMemcachedPort},
155            {cert_file, Path},
156            {private_key_file, Path},
157            {cacert_file, CACertPath},
158            {ssl_minimum_protocol, ns_ssl_services_setup:ssl_minimum_protocol()}],
159
160    ErlangArgs = ["-smp", "enable",
161                  "+P", "327680",
162                  "+K", "true",
163                  "-kernel", "error_logger", "false",
164                  "-sasl", "sasl_error_logger", "false",
165                  "-nouser",
166                  "-run", "child_erlang", "child_start", "ns_ssl_proxy"],
167
168    create_erl_node_spec(xdcr_proxy, Args, "NS_SSL_PROXY_ENV_ARGS", ErlangArgs).
169
170create_erl_node_spec(Type, Args, EnvArgsVar, ErlangArgs) ->
171    PathArgs = ["-pa"] ++ lists:reverse(code:get_path()),
172    EnvArgsTail = [{K, V}
173                   || {K, V} <- application:get_all_env(ns_server),
174                      case atom_to_list(K) of
175                          "error_logger" ++ _ -> true;
176                          "path_config" ++ _ -> true;
177                          "dont_suppress_stderr_logger" -> true;
178                          "loglevel_" ++ _ -> true;
179                          "disk_sink_opts" -> true;
180                          "ssl_ciphers" -> true;
181                          "net_kernel_verbosity" -> true;
182                          _ -> false
183                      end],
184    EnvArgs = Args ++ EnvArgsTail,
185
186    AllArgs = PathArgs ++ ErlangArgs,
187
188    ErlPath = filename:join([hd(proplists:get_value(root, init:get_arguments())),
189                             "bin", "erl"]),
190
191    Env0 = case os:getenv("ERL_CRASH_DUMP_BASE") of
192               false ->
193                   [];
194               Base ->
195                   [{"ERL_CRASH_DUMP", Base ++ "." ++ atom_to_list(Type)}]
196           end,
197
198    Env = [{EnvArgsVar, misc:inspect_term(EnvArgs)} | Env0],
199
200    Options0 = [use_stdio, {env, Env}],
201    Options =
202        case misc:get_env_default(dont_suppress_stderr_logger, false) of
203            true ->
204                [ns_server_no_stderr_to_stdout | Options0];
205            false ->
206                Options0
207        end,
208
209    {Type, ErlPath, AllArgs, Options}.
210
211per_bucket_moxi_specs(Config) ->
212    case ns_cluster_membership:should_run_service(Config, kv, node()) of
213        true ->
214            do_per_bucket_moxi_specs(Config);
215        false ->
216            []
217    end.
218
219do_per_bucket_moxi_specs(Config) ->
220    BucketConfigs = ns_bucket:get_buckets(Config),
221    RestPort = ns_config:search_node_prop(Config, rest, port),
222    Command = path_config:component_path(bin, "moxi"),
223    lists:foldl(
224      fun ({BucketName, BucketConfig}, Acc) ->
225              case proplists:get_value(moxi_port, BucketConfig) of
226                  undefined ->
227                      Acc;
228                  Port ->
229                      LittleZ =
230                          lists:flatten(
231                            io_lib:format(
232                              "url=http://127.0.0.1:~B/pools/default/"
233                              "bucketsStreaming/~s",
234                              [RestPort, BucketName])),
235                      BigZ =
236                          lists:flatten(
237                            io_lib:format(
238                              "port_listen=~B,downstream_max=1024,downstream_conn_max=4,"
239                              "connect_max_errors=5,connect_retry_interval=30000,"
240                              "connect_timeout=400,"
241                              "auth_timeout=100,cycle=200,"
242                              "downstream_conn_queue_timeout=200,"
243                              "downstream_timeout=5000,wait_queue_timeout=200",
244                              [Port])),
245                      Args = ["-B", "auto", "-z", LittleZ, "-Z", BigZ,
246                              "-p", "0", "-Y", "y", "-O", "stderr"],
247                      Passwd = proplists:get_value(sasl_password, BucketConfig,
248                                                   ""),
249                      Opts = [use_stdio, stderr_to_stdout,
250                              {env, [{"MOXI_SASL_PLAIN_USR", BucketName},
251                                     {"MOXI_SASL_PLAIN_PWD", Passwd},
252                                     {"http_proxy", ""}]}],
253                      [{{moxi, BucketName}, Command, Args, Opts}|Acc]
254              end
255      end, [], BucketConfigs).
256
257dynamic_children(Mode) ->
258    Config = ns_config:get(),
259
260    Specs = do_dynamic_children(Mode, Config),
261    expand_specs(lists:flatten(Specs), Config).
262
263do_dynamic_children(shutdown, Config) ->
264    [memcached_spec(),
265     moxi_spec(Config),
266     saslauthd_port_spec(Config),
267     per_bucket_moxi_specs(Config),
268     maybe_create_ssl_proxy_spec(Config)];
269do_dynamic_children(normal, Config) ->
270    [memcached_spec(),
271     moxi_spec(Config),
272     kv_node_projector_spec(Config),
273     index_node_spec(Config),
274     query_node_spec(Config),
275     saslauthd_port_spec(Config),
276     goxdcr_spec(Config),
277     per_bucket_moxi_specs(Config),
278     maybe_create_ssl_proxy_spec(Config),
279     fts_spec(Config),
280     example_service_spec(Config)].
281
282expand_specs(Specs, Config) ->
283    [expand_args(S, Config) || S <- Specs].
284
285query_node_spec(Config) ->
286    case ns_cluster_membership:should_run_service(Config, n1ql, node()) of
287        false ->
288            [];
289        _ ->
290            RestPort = misc:node_rest_port(Config, node()),
291            Command = path_config:component_path(bin, "cbq-engine"),
292            DataStoreArg = "--datastore=http://127.0.0.1:" ++ integer_to_list(RestPort),
293            CnfgStoreArg = "--configstore=http://127.0.0.1:" ++ integer_to_list(RestPort),
294            HttpArg = "--http=:" ++ integer_to_list(query_rest:get_query_port(Config, node())),
295            EntArg = "--enterprise=" ++ atom_to_list(cluster_compat_mode:is_enterprise()),
296
297            HttpsArgs = case query_rest:get_ssl_query_port(Config, node()) of
298                            undefined ->
299                                [];
300                            Port ->
301                                ["--https=:" ++ integer_to_list(Port),
302                                 "--certfile=" ++ ns_ssl_services_setup:memcached_cert_path(),
303                                 "--keyfile=" ++ ns_ssl_services_setup:memcached_key_path(),
304                                 "--ssl_minimum_protocol=" ++
305                                     atom_to_list(ns_ssl_services_setup:ssl_minimum_protocol())]
306                        end,
307            Spec = {'query', Command,
308                    [DataStoreArg, HttpArg, CnfgStoreArg, EntArg] ++ HttpsArgs,
309                    [via_goport, exit_status, stderr_to_stdout,
310                     {env, build_go_env_vars(Config, 'cbq-engine') ++
311                          build_tls_config_env_var(Config)},
312                     {log, ?QUERY_LOG_FILENAME}]},
313
314            [Spec]
315    end.
316
317find_executable(Name) ->
318    K = list_to_atom("ns_ports_setup-" ++ Name ++ "-available"),
319    case erlang:get(K) of
320        undefined ->
321            Cmd = path_config:component_path(bin, Name),
322            RV = os:find_executable(Cmd),
323            erlang:put(K, RV),
324            RV;
325        V ->
326            V
327    end.
328
329kv_node_projector_spec(Config) ->
330    ProjectorCmd = find_executable("projector"),
331    case ProjectorCmd =/= false andalso
332        ns_cluster_membership:should_run_service(Config, kv, node()) of
333        false ->
334            [];
335        _ ->
336            % Projector is a component that is required by 2i
337            ProjectorPort = ns_config:search(Config, {node, node(), projector_port}, 9999),
338            RestPort = misc:node_rest_port(Config, node()),
339            LocalMemcachedPort = ns_config:search_node_prop(node(), Config, memcached, port),
340            MinidumpDir = path_config:minidump_dir(),
341
342            Args = ["-kvaddrs=127.0.0.1:" ++ integer_to_list(LocalMemcachedPort),
343                    "-adminport=:" ++ integer_to_list(ProjectorPort),
344                    "-diagDir=" ++ MinidumpDir,
345                    "127.0.0.1:" ++ integer_to_list(RestPort)],
346
347            Spec = {'projector', ProjectorCmd, Args,
348                    [via_goport, exit_status, stderr_to_stdout,
349                     {log, ?PROJECTOR_LOG_FILENAME},
350                     {env, build_go_env_vars(Config, projector)}]},
351            [Spec]
352    end.
353
354goxdcr_spec(Config) ->
355    Cmd = find_executable("goxdcr"),
356
357    case cluster_compat_mode:is_goxdcr_enabled(Config) andalso
358        Cmd =/= false of
359        false ->
360            [];
361        true ->
362            create_goxdcr_spec(Config, Cmd, false)
363    end.
364
365create_goxdcr_spec(Config, Cmd, Upgrade) ->
366    AdminPort = "-sourceKVAdminPort=" ++
367        integer_to_list(misc:node_rest_port(Config, node())),
368    XdcrRestPort = "-xdcrRestPort=" ++
369        integer_to_list(ns_config:search(Config, {node, node(), xdcr_rest_port}, 9998)),
370    IsEnterprise = "-isEnterprise=" ++ atom_to_list(cluster_compat_mode:is_enterprise()),
371
372    Args0 = [AdminPort, XdcrRestPort, IsEnterprise],
373
374    Args1 = case Upgrade of
375                true ->
376                    ["-isConvert=true" | Args0];
377                false ->
378                    Args0
379            end,
380
381    UpstreamPort = ns_config:search(Config, {node, node(), ssl_proxy_upstream_port}, undefined),
382    Args =
383        case UpstreamPort of
384            undefined ->
385                Args1;
386            _ ->
387                LocalProxyPort = "-localProxyPort=" ++ integer_to_list(UpstreamPort),
388                [LocalProxyPort | Args1]
389        end,
390
391    [{'goxdcr', Cmd, Args,
392      [via_goport, exit_status, stderr_to_stdout,
393       {log, ?GOXDCR_LOG_FILENAME},
394       {env, build_go_env_vars(Config, goxdcr)}]}].
395
396create_goxdcr_upgrade_spec(Config) ->
397    Cmd = find_executable("goxdcr"),
398    true = Cmd =/= false,
399    [Spec] = create_goxdcr_spec(Config, Cmd, true),
400    Spec.
401
402index_node_spec(Config) ->
403    case ns_cluster_membership:should_run_service(Config, index, node()) of
404        false ->
405            [];
406        _ ->
407            NumVBuckets = case ns_config:search(couchbase_num_vbuckets_default) of
408                              false -> misc:getenv_int("COUCHBASE_NUM_VBUCKETS", 1024);
409                              {value, X} -> X
410                          end,
411            IndexerCmd = path_config:component_path(bin, "indexer"),
412            RestPort = misc:node_rest_port(Config, node()),
413            AdminPort = ns_config:search(Config, {node, node(), indexer_admin_port}, 9100),
414            ScanPort = ns_config:search(Config, {node, node(), indexer_scan_port}, 9101),
415            HttpPort = ns_config:search(Config, {node, node(), indexer_http_port}, 9102),
416            StInitPort = ns_config:search(Config, {node, node(), indexer_stinit_port}, 9103),
417            StCatchupPort = ns_config:search(Config, {node, node(), indexer_stcatchup_port}, 9104),
418            StMaintPort = ns_config:search(Config, {node, node(), indexer_stmaint_port}, 9105),
419            {ok, IdxDir} = ns_storage_conf:this_node_ixdir(),
420            IdxDir2 = filename:join(IdxDir, "@2i"),
421            MinidumpDir = path_config:minidump_dir(),
422            NodeUUID = binary_to_list(ns_config:uuid()),
423            HttpsArgs = case ns_config:search(Config, {node, node(), indexer_https_port}, undefined) of
424                            undefined ->
425                                [];
426                            Port ->
427                                ["--httpsPort=" ++ integer_to_list(Port),
428                                 "--certFile=" ++ ns_ssl_services_setup:memcached_cert_path(),
429                                 "--keyFile=" ++ ns_ssl_services_setup:memcached_key_path()]
430                        end,
431
432            Spec = {'indexer', IndexerCmd,
433                    ["-vbuckets=" ++ integer_to_list(NumVBuckets),
434                     "-cluster=127.0.0.1:" ++ integer_to_list(RestPort),
435                     "-adminPort=" ++ integer_to_list(AdminPort),
436                     "-scanPort=" ++ integer_to_list(ScanPort),
437                     "-httpPort=" ++ integer_to_list(HttpPort),
438                     "-streamInitPort=" ++ integer_to_list(StInitPort),
439                     "-streamCatchupPort=" ++ integer_to_list(StCatchupPort),
440                     "-streamMaintPort=" ++ integer_to_list(StMaintPort),
441                     "-storageDir=" ++ IdxDir2,
442                     "-diagDir=" ++ MinidumpDir,
443                     "-nodeUUID=" ++ NodeUUID,
444                     "-isEnterprise=" ++ atom_to_list(cluster_compat_mode:is_enterprise())] ++ HttpsArgs,
445                    [via_goport, exit_status, stderr_to_stdout,
446                     {log, ?INDEXER_LOG_FILENAME},
447                     {env, build_go_env_vars(Config, index)}]},
448            [Spec]
449    end.
450
451build_go_env_vars(Config, RPCService) ->
452    GoTraceBack0 = ns_config:search(ns_config:latest(), gotraceback, <<"crash">>),
453    GoTraceBack = binary_to_list(GoTraceBack0),
454    [{"GOTRACEBACK", GoTraceBack} | build_cbauth_env_vars(Config, RPCService)].
455
456build_tls_config_env_var(Config) ->
457    [{"CBAUTH_TLS_CONFIG",
458      binary_to_list(ejson:encode(
459                       {[{minTLSVersion, ns_ssl_services_setup:ssl_minimum_protocol(Config)},
460                         {ciphersStrength, ns_ssl_services_setup:ciphers_strength(Config)}]}))}].
461
462build_cbauth_env_vars(Config, RPCService) ->
463    true = (RPCService =/= undefined),
464    RestPort = misc:node_rest_port(Config, node()),
465    User = mochiweb_util:quote_plus(ns_config_auth:get_user(special)),
466    Password = mochiweb_util:quote_plus(ns_config_auth:get_password(special)),
467
468    URL0 = io_lib:format("http://~s:~s@127.0.0.1:~b/~s",
469                         [User, Password, RestPort, RPCService]),
470    URL = lists:flatten(URL0),
471
472    [{"CBAUTH_REVRPC_URL", URL}].
473
474saslauthd_port_spec(Config) ->
475    Cmd = find_executable("saslauthd-port"),
476    case Cmd =/= false of
477        true ->
478            [{saslauthd_port, Cmd, [],
479              [use_stdio, exit_status, stderr_to_stdout,
480               {env, build_go_env_vars(Config, saslauthd)}]}];
481        _ ->
482            []
483    end.
484
485expand_args({Name, Cmd, ArgsIn, OptsIn}, Config) ->
486    %% Expand arguments
487    Args0 = lists:map(fun ({Format, Keys}) ->
488                              format(Config, Name, Format, Keys);
489                          (X) -> X
490                      end,
491                      ArgsIn),
492    Args = Args0 ++ ns_config:search(Config, {node, node(), {Name, extra_args}}, []),
493    %% Expand environment variables within OptsIn
494    Opts = lists:map(
495             fun ({env, Env}) ->
496                     {env, lists:map(
497                             fun ({Var, {Format, Keys}}) ->
498                                     {Var, format(Config, Name, Format, Keys)};
499                                 (X) -> X
500                             end, Env)};
501                 (X) -> X
502             end, OptsIn),
503    {Name, Cmd, Args, Opts}.
504
505format(Config, Name, Format, Keys) ->
506    Values = lists:map(fun ({Module, FuncName, Args}) -> erlang:apply(Module, FuncName, Args);
507                           ({Key, SubKey}) -> ns_config:search_node_prop(Config, Key, SubKey);
508                           (Key) -> ns_config:search_node_prop(Config, Name, Key)
509                       end, Keys),
510    lists:flatten(io_lib:format(Format, Values)).
511
512default_is_passwordless(Config) ->
513    lists:member({"default", local}, menelaus_users:get_passwordless()) andalso
514        lists:keymember("default", 1, ns_bucket:get_buckets(Config)).
515
516should_run_moxi(Config) ->
517    ns_cluster_membership:should_run_service(Config, kv, node())
518        andalso
519          ((not cluster_compat_mode:is_cluster_50(Config)) orelse
520           default_is_passwordless(Config)).
521
522moxi_spec(Config) ->
523    case should_run_moxi(Config) of
524        true ->
525            do_moxi_spec();
526        false ->
527            []
528    end.
529
530do_moxi_spec() ->
531    {moxi, path_config:component_path(bin, "moxi"),
532     ["-Z", {"port_listen=~B,default_bucket_name=default,downstream_max=1024,downstream_conn_max=4,"
533             "connect_max_errors=5,connect_retry_interval=30000,"
534             "connect_timeout=400,"
535             "auth_timeout=100,cycle=200,"
536             "downstream_conn_queue_timeout=200,"
537             "downstream_timeout=5000,wait_queue_timeout=200",
538             [port]},
539      "-z", {"url=http://127.0.0.1:~B/pools/default/saslBucketsStreaming?moxi=1",
540             [{misc, this_node_rest_port, []}]},
541      "-p", "0",
542      "-Y", "y",
543      "-O", "stderr",
544      {"~s", [verbosity]}
545     ],
546     [{env, [{"EVENT_NOSELECT", "1"},
547             {"MOXI_SASL_PLAIN_USR", {"~s", [{ns_moxi_sup, rest_user, []}]}},
548             {"MOXI_SASL_PLAIN_PWD", {"~s", [{ns_moxi_sup, rest_pass, []}]}},
549             {"http_proxy", ""}
550            ]},
551      use_stdio, exit_status,
552      stderr_to_stdout,
553      stream]
554    }.
555
556memcached_spec() ->
557    {memcached, path_config:component_path(bin, "memcached"),
558     ["-C", {"~s", [{memcached, config_path}]}],
559     [{env, [{"EVENT_NOSELECT", "1"},
560             %% NOTE: bucket engine keeps this number of top keys
561             %% per top-keys-shard. And number of shards is hard-coded to 8
562             %%
563             %% So with previous setting of 100 we actually got 800
564             %% top keys every time. Even if we need just 10.
565             %%
566             %% See hot_keys_keeper.erl TOP_KEYS_NUMBER constant
567             %%
568             %% Because of that heavy sharding we cannot ask for
569             %% very small number, which would defeat usefulness
570             %% LRU-based top-key maintenance in memcached. 5 seems
571             %% not too small number which means that we'll deal
572             %% with 40 top keys.
573             {"MEMCACHED_TOP_KEYS", "5"},
574             {"CBSASL_PWFILE", {"~s", [{isasl, path}]}}]},
575      use_stdio,
576      stderr_to_stdout, exit_status,
577      port_server_dont_start,
578      stream]
579    }.
580
581fts_spec(Config) ->
582    FtCmd = find_executable("cbft"),
583    NodeUUID = ns_config:search(Config, {node, node(), uuid}, false),
584    case FtCmd =/= false andalso
585        NodeUUID =/= false andalso
586        ns_cluster_membership:should_run_service(Config, fts, node()) of
587        false ->
588            [];
589        _ ->
590            NsRestPort = misc:node_rest_port(Config, node()),
591            FtRestPort = ns_config:search(Config, {node, node(), fts_http_port}, 8094),
592            {ok, IdxDir} = ns_storage_conf:this_node_ixdir(),
593            FTSIdxDir = filename:join(IdxDir, "@fts"),
594            ok = misc:ensure_writable_dir(FTSIdxDir),
595            {_, Host} = misc:node_name_host(node()),
596            BindHttp = io_lib:format("~s:~b,0.0.0.0:~b", [Host, FtRestPort, FtRestPort]),
597            BindHttps = case ns_config:search(Config, {node, node(), fts_ssl_port}, undefined) of
598                            undefined ->
599                                [];
600                            Port ->
601                                ["-bindHttps=:" ++ integer_to_list(Port),
602                                 "-tlsCertFile=" ++ ns_ssl_services_setup:memcached_cert_path(),
603                                 "-tlsKeyFile=" ++ ns_ssl_services_setup:memcached_key_path()]
604                        end,
605            {ok, FTSMemoryQuota} = ns_storage_conf:get_memory_quota(Config, fts),
606            MaxReplicasAllowed = case cluster_compat_mode:is_enterprise() of
607                                     true -> 3;
608                                     false -> 0
609                                 end,
610            BucketTypesAllowed = case cluster_compat_mode:is_enterprise() of
611                                     true -> "membase:ephemeral";
612                                     false -> "membase"
613                                 end,
614            Options = "startCheckServer=skip," ++
615                      "slowQueryLogTimeout=5s," ++
616                      "defaultMaxPartitionsPerPIndex=171," ++
617                      "bleveMaxResultWindow=10000," ++
618                      "failoverAssignAllPrimaries=false," ++
619                      "hideUI=true," ++
620                      "cbaudit=" ++ atom_to_list(cluster_compat_mode:is_enterprise()) ++ "," ++
621                      "ftsMemoryQuota=" ++ integer_to_list(FTSMemoryQuota * 1024000) ++ "," ++
622                      "maxReplicasAllowed=" ++ integer_to_list(MaxReplicasAllowed) ++ "," ++
623                      "bucketTypesAllowed=" ++ BucketTypesAllowed,
624            Spec = {fts, FtCmd,
625                    [
626                     "-cfg=metakv",
627                     "-uuid=" ++ NodeUUID,
628                     "-server=http://127.0.0.1:" ++ integer_to_list(NsRestPort),
629                     "-bindHttp=" ++ BindHttp,
630                     "-dataDir=" ++ FTSIdxDir,
631                     "-tags=feed,janitor,pindex,queryer,cbauth_service",
632                     "-auth=cbauth",
633                     "-extra=" ++ io_lib:format("~s:~b", [Host, NsRestPort]),
634                     "-options=" ++ Options
635                    ] ++ BindHttps,
636                    [via_goport, exit_status, stderr_to_stdout,
637                     {log, ?FTS_LOG_FILENAME},
638                     {env, build_go_env_vars(Config, fts) ++ build_tls_config_env_var(Config)}]},
639            [Spec]
640    end.
641
642example_service_spec(Config) ->
643    CacheCmd = find_executable("cache-service"),
644    NodeUUID = ns_config:search(Config, {node, node(), uuid}, false),
645
646    case CacheCmd =/= false andalso
647        NodeUUID =/= false andalso
648        ns_cluster_membership:should_run_service(Config, example, node()) of
649        true ->
650            Port = misc:node_rest_port(Config, node()) + 20000,
651            {_, Host} = misc:node_name_host(node()),
652            Args = ["-node-id", binary_to_list(NodeUUID),
653                    "-host", Host ++ ":" ++ integer_to_list(Port)],
654            Spec = {example, CacheCmd, Args,
655                    [via_goport, exit_status, stderr_to_stdout,
656                     {env, build_go_env_vars(Config, example)}]},
657            [Spec];
658        false ->
659            []
660    end.
661
662run_cbsasladm(Iterations) ->
663    Args = ["-i", integer_to_list(Iterations), "pwconv", "-", "-"],
664
665    {ok, P} =
666        goport:start_link(find_executable("cbsasladm"),
667                          [exit_status, graceful_shutdown, stderr_to_stdout,
668                           stream, binary,
669                           {args, Args},
670                           {name, false}]),
671    P.
672