diff options
author | Jan Lehnardt <jan@apache.org> | 2014-11-06 17:33:43 +0100 |
---|---|---|
committer | Jan Lehnardt <jan@apache.org> | 2014-11-06 17:33:43 +0100 |
commit | 277ca66441bf56f93132137c0e8d7d1337c99f06 (patch) | |
tree | a75731acd918eac113731e6c53ff87e91929f647 | |
parent | bc41677484785f407981c91a2525e2778885f37b (diff) | |
download | couchdb-277ca66441bf56f93132137c0e8d7d1337c99f06.tar.gz |
wip: implement setup handling
-rw-r--r-- | src/setup.erl | 159 | ||||
-rw-r--r-- | src/setup_httpd.erl | 73 |
2 files changed, 219 insertions, 13 deletions
diff --git a/src/setup.erl b/src/setup.erl new file mode 100644 index 000000000..4a4524c43 --- /dev/null +++ b/src/setup.erl @@ -0,0 +1,159 @@ +% 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(setup). + +-export([enable_cluster/1, finish_cluster/0, add_node/1]). +-include_lib("../couch/include/couch_db.hrl"). + + +require_admins(undefined, {undefined, undefined}) -> + % no admin in CouchDB, no admin in request + throw({error, "Cluster setup requires admin account to be configured"}); +require_admins(_,_) -> + ok. + +error_bind_address() -> + throw({error, "Cluster setup requires bind_addres != 127.0.0.1"}). + +require_bind_address("127.0.0.1", undefined) -> + error_bind_address(); +require_bind_address("127.0.0.1", <<"127.0.0.1">>) -> + error_bind_address(); +require_bind_address(_, _) -> + ok. + +is_cluster_enabled() -> + % bind_address != 127.0.0.1 AND admins != empty + BindAddress = config:get("httpd", "bind_address"), + Admins = config:get("admins"), + case {BindAddress, Admins} of + {"127.0.0.1", _} -> no; + {_,[]} -> no; + {_,_} -> ok + end. + + +has_cluster_system_dbs() -> + % GET /_users /_replicator /_cassim + + Users = fabric:get_db_info("_users"), + Replicator = fabric:get_db_info("_replicator"), + Cassim = fabric:get_db_info("_cassim"), + case {Users, Replicator, Cassim} of + {{ok, _}, {ok, _}, {ok, _}} -> ok; + _Else -> no + end. + +enable_cluster(Options) -> + enable_cluster_int(Options, is_cluster_enabled()). + +enable_cluster_int(_Options, ok) -> + {error, cluster_enabled}; +enable_cluster_int(Options, no) -> + + % if no admin in config and no admin in req -> error + CurrentAdmins = config:get("admins"), + NewCredentials = { + proplists:get_value(username, Options), + proplists:get_value(password, Options) + }, + + % if bind_address == 127.0.0.1 and no bind_address in req -> error + CurrentBindAddress = config:get("httpd","bind_address"), + NewBindAddress = proplists:get_value(bind_address, Options), + ok = require_admins(CurrentAdmins, NewCredentials), + ok = require_bind_address(CurrentBindAddress, NewBindAddress), + + case NewCredentials of + {undefined, undefined} -> + ok; + {Username, Password} -> + % TODO check if this gets hashed + config:set("admins", binary_to_list(Username), binary_to_list(Password)) + end, + + case NewBindAddress of + undefined -> + config:set("httpd", "bind_address", "0.0.0.0"); + NewBindAddress -> + config:set("httpd", "bind_address", binary_to_list(NewBindAddress)) + end, + + Port = proplists:get_value(port, Options), + case Port of + undefined -> + ok; + Port -> + config:set("httpd", "port", integer_to_list(Port)) + end, + io:format("~nEnable Cluster: ~p~n", [Options]). + %cluster_state:set(enabled). + +finish_cluster() -> + finish_cluster_int(has_cluster_system_dbs()). +finish_cluster_int(no) -> + {error, cluster_finished}; +finish_cluster_int(ok) -> + io:format("~nFinish Cluster~n"). + % create clustered databases (_users, _replicator, _cassim/_metadata + % am I in enabled mode, are there nodes? + + +add_node(Options) -> + add_node_int(Options, is_cluster_enabled()). + +add_node_int(_Options, no) -> + {error, cluster_not_enabled}; +add_node_int(Options, ok) -> + io:format("~nadd node: ~p~n", [Options]), + ErlangCookie = erlang:get_cookie(), + + % POST to nodeB/_setup + RequestOptions = [ + {basic_auth, { + proplists:get_value(username, Options), + proplists:get_value(password, Options) + }} + ], + + Body = ?JSON_ENCODE({[ + {<<"action">>, <<"receive_cookie">>}, + {<<"cookie">>, atom_to_binary(ErlangCookie, utf8)} + ]}), + + Headers = [ + {"Content-Type","application/json"} + ], + + Host = proplists:get_value(host, Options), + Port = proplists:get_value(port, Options, <<"5984">>), + Url = binary_to_list(<<"http://", Host/binary, ":", Port/binary, "/_setup">>), + + io:format("~nUrl: ~p~n", [Url]), + io:format("~nBody: ~p~n", [Body]), + + case ibrowse:send_req(Url, Headers, post, Body, RequestOptions) of + {ok, 200, _, _} -> + % when done, PUT :5986/nodes/nodeB + create_node_doc(Host, Port); + Else -> + io:format("~nsend_req: ~p~n", [Else]), + Else + end. + + +create_node_doc(Host, Port) -> + {ok, Db} = couch_db:open_int("nodes"), + Doc = {[{<<"_id">>, <<Host/binary, ":", Port/binary>>}]}, + Options = [], + couch_db:update_doc(Db, Doc, Options). diff --git a/src/setup_httpd.erl b/src/setup_httpd.erl index a5eb6441b..e0751d210 100644 --- a/src/setup_httpd.erl +++ b/src/setup_httpd.erl @@ -29,25 +29,72 @@ handle_setup_req(Req) -> end. +get_options(Options, Setup) -> + ExtractValues = fun({Tag, Option}, OptionsAcc) -> + case couch_util:get_value(Option, Setup) of + undefined -> OptionsAcc; + Value -> [{Tag, Value} | OptionsAcc] + end + end, + lists:foldl(ExtractValues, [], Options). + handle_action("enable_cluster", Setup) -> - io:format("~nenable_cluster: ~p~n", [Setup]); - % if admin.username && admin.password - % create admin - % if bind_address - % set bind_address - % else - % bind_address to 0.0.0.0 - % if port - % set port - % set cluster_state to cluster_enabled + Options = get_options([ + {username, <<"username">>}, + {password, <<"password">>}, + {bind_address, <<"bind_address">>}, + {port, <<"port">>} + ], Setup), + case setup:enable_cluster(Options) of + {error, cluster_enabled} -> + {error, <<"Cluster is already enabled">>}; + _ -> ok + end; + + handle_action("finish_cluster", Setup) -> - io:format("~nfinish_cluster: ~p~n", [Setup]); - % create clustered databases (_users, _replicator, _cassim/_metadata + io:format("~nfinish_cluster: ~p~n", [Setup]), + case etup:finish_cluster() of + {error, cluster_finished} -> + {error, <<"Cluster is already finished">>}; + _ -> ok + end; handle_action("add_node", Setup) -> - io:format("~nadd_node: ~p~n", [Setup]); + io:format("~nadd_node: ~p~n", [Setup]), + + Options = get_options([ + {username, <<"username">>}, + {password, <<"password">>}, + {host, <<"host">>}, + {port, <<"port">>} + ], Setup), + case setup:add_node(Options) of + {error, cluster_not_enabled} -> + {error, <<"Cluster is not enabled.">>}; + {error, {conn_failed, {error, econnrefused}}} -> + {error, <<"Add node failed. Invalid Host and/or Port.">>}; + {error, wrong_credentials} -> + {error, <<"Add node failed. Invalid admin credentials,">>}; + {error, Message} -> + {error, Message}; + _ -> ok + end; + handle_action("remove_node", Setup) -> io:format("~nremove_node: ~p~n", [Setup]); + +handle_action("receive_cookie", Setup) -> + io:format("~nreceive_cookie: ~p~n", [Setup]), + Options = get_options([ + {cookue, <<"cookie">>} + ], Setup), + case setup:receive_cookie(Options) of + {error, Error} -> + {error, Error}; + _ -> ok + end; + handle_action(_, _) -> io:format("~ninvalid_action: ~n", []), {error, <<"Invalid Action'">>}. |