couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject [17/23] couchdb-setup git commit: wip: implement setup handling
Date Fri, 07 Nov 2014 14:46:41 GMT
wip: implement setup handling


Project: http://git-wip-us.apache.org/repos/asf/couchdb-setup/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-setup/commit/277ca664
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-setup/tree/277ca664
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-setup/diff/277ca664

Branch: refs/heads/master
Commit: 277ca66441bf56f93132137c0e8d7d1337c99f06
Parents: bc41677
Author: Jan Lehnardt <jan@apache.org>
Authored: Thu Nov 6 17:33:43 2014 +0100
Committer: Jan Lehnardt <jan@apache.org>
Committed: Thu Nov 6 17:33:43 2014 +0100

----------------------------------------------------------------------
 src/setup.erl       | 159 +++++++++++++++++++++++++++++++++++++++++++++++
 src/setup_httpd.erl |  73 ++++++++++++++++++----
 2 files changed, 219 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-setup/blob/277ca664/src/setup.erl
----------------------------------------------------------------------
diff --git a/src/setup.erl b/src/setup.erl
new file mode 100644
index 0000000..4a4524c
--- /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).

http://git-wip-us.apache.org/repos/asf/couchdb-setup/blob/277ca664/src/setup_httpd.erl
----------------------------------------------------------------------
diff --git a/src/setup_httpd.erl b/src/setup_httpd.erl
index a5eb644..e0751d2 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'">>}.


Mime
View raw message