summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md98
-rw-r--r--src/setup.erl21
-rw-r--r--src/setup_httpd.erl6
-rwxr-xr-xtest/t-frontend-setup.sh9
-rwxr-xr-xtest/t.sh9
5 files changed, 82 insertions, 61 deletions
diff --git a/README.md b/README.md
index 7043b0f84..a6c6d184e 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,14 @@ From here on, there are two paths, one is via Fauxton (a) the other is
using a HTTP endpoint (b). Fauxton just uses the HTTP endpoint in (b).
(b) can be used to set up a cluster programmatically.
+When using (b) you POST HTTP requests with a JSON request body (the request content type has to be set to application/json).
+
+If you have already setup a server admin account, you might need to pass the credentials to the HTTP calls using HTTP basic authentication.
+Alternativaly, if you use the cURL command you can can add username and password inline, like so:
+
+```
+curl -X PUT "http://admin:password@127.0.0.1:5984/mydb"
+```
2.a. Go to Fauxton. There is a “Cluster Setup” tab in the sidebar. Go
to the tab and get presented with a form that asks you to enter an admin
@@ -43,23 +51,21 @@ is no need to ask for admin credentials here. If the bind_address is !=
127.0.0.1, we can skip this entirely and Fauxton can show the add_node
UI right away.
-- POST to /_setup with
+- POST a JSON entity to /_cluster_setup, the entity looks like:
```
- {
- "action": "enable_cluster",
- "admin": {
- "user": "username",
- "pass": "password"
- },
- ["bind_address": "xxxx",]
- ["port": yyyy]
- }
+{
+ "action":"enable_cluster",
+ "username":"username",
+ "password":"password",
+ "bind_address":"0.0.0.0",
+ "port": 5984
+}
```
This sets up the admin user on the current node and binds to 0.0.0.0:5984
or the specified ip:port. Logs admin user into Fauxton automatically.
-2.b. POST to /_setup as shown above.
+2.b. POST to /_cluster_setup as shown above.
Repeat on all nodes.
- keep the same username/password everywhere.
@@ -75,23 +81,21 @@ Repeat on all nodes.
a. Go to Fauxton / Cluster Setup, once we have enabled the cluster, the
UI shows an “Add Node” interface with the fields admin, and node:
-- POST to /_setup with
+- POST a JSON entity to /_cluster_setup, the entity looks like:
```
- {
- "action": "add_node",
- "admin": { // should be auto-filled from Fauxton, store plaintext PW in
- // localStorage until we finish_cluster or timeout.
- "user": "username",
- "pass": "password"
- },
- "node": {
- "host": "hostname",
- ["port": 5984]
- }
- }
+{
+ "action":"add_node",
+ "username":"username",
+ "password":"password",
+ "host":"192.168.1.100",
+ ["port": 5984],
+ "name": "node1" // as in “node1@hostname”, same as in vm.args
+}
```
-b. as in a, but without the Fauxton bits, just POST to /_setup
+In the example above, this adds the node with IP address 192.168.1.100 to the cluster.
+
+b. as in a, but without the Fauxton bits, just POST to /_cluster_setup
- this request will do this:
- on the “setup coordination node”:
- check if we have an Erlang Cookie Secret. If not, generate
@@ -99,7 +103,7 @@ b. as in a, but without the Fauxton bits, just POST to /_setup
- store the cookie in config.ini, re-set_cookie() on startup.
- make a POST request to the node specified in the body above
using the admin credentials in the body above:
- POST to http://username:password@node_b:5984/_setup with:
+ POST to http://username:password@node_b:5984/_cluster_setup with:
```
{
"action": "receive_cookie",
@@ -119,10 +123,10 @@ b. as in a, but without the Fauxton bits, just POST to /_setup
4.a. When all nodes are added, click the [Finish Cluster Setup] button
in Fauxton.
-- this does POST /_setup
+- this does POST /_cluster_setup
```
{
- "action": "finish_setup"
+ "action": "finish_cluster"
}
```
@@ -144,41 +148,41 @@ This is right after starting a node for the first time, and any time
before the cluster is enabled as outlined above.
```
-GET /_setup
+GET /_cluster_setup
{"state": "cluster_disabled"}
-POST /_setup {"action":"enable_cluster"...} -> Transition to State 2
-POST /_setup {"action":"enable_cluster"...} with empty admin user/pass or invalid host/post or host/port not available -> Error
-POST /_setup {"action":"anything_but_enable_cluster"...} -> Error
+POST /_cluster_setup {"action":"enable_cluster"...} -> Transition to State 2
+POST /_cluster_setup {"action":"enable_cluster"...} with empty admin user/pass or invalid host/post or host/port not available -> Error
+POST /_cluster_setup {"action":"anything_but_enable_cluster"...} -> Error
```
### State 2: Cluster enabled, admin user set, waiting for nodes to be added.
```
-GET /_setup
+GET /_cluster_setup
{"state":"cluster_enabled","nodes":[]}
-POST /_setup {"action":"enable_cluster"...} -> Error
-POST /_setup {"action":"add_node"...} -> Stay in State 2, but return "nodes":["node B"}] on GET
-POST /_setup {"action":"add_node"...} -> if target node not available, Error
-POST /_setup {"action":"finish_cluster"} with no nodes set up -> Error
-POST /_setup {"action":"finish_cluster"} -> Transition to State 3
-POST /_setup {"action":"delete_node"...} -> Stay in State 2, but delete node from /nodes, reflect the change in GET /_setup
-POST /_setup {"action":"delete_node","node":"unknown"} -> Error Unknown Node
+POST /_cluster_setup {"action":"enable_cluster"...} -> Error
+POST /_cluster_setup {"action":"add_node"...} -> Stay in State 2, but return "nodes":["node B"}] on GET
+POST /_cluster_setup {"action":"add_node"...} -> if target node not available, Error
+POST /_cluster_setup {"action":"finish_cluster"} with no nodes set up -> Error
+POST /_cluster_setup {"action":"finish_cluster"} -> Transition to State 3
+POST /_cluster_setup {"action":"delete_node"...} -> Stay in State 2, but delete node from /nodes, reflect the change in GET /_cluster_setup
+POST /_cluster_setup {"action":"delete_node","node":"unknown"} -> Error Unknown Node
```
### State 3: Cluster set up, all nodes operational
```
-GET /_setup
+GET /_cluster_setup
{"state":"cluster_finished","nodes":["node a", "node b", ...]}
-POST /_setup {"action":"enable_cluster"...} -> Error
-POST /_setup {"action":"finish_cluster"...} -> Stay in State 3, do nothing
-POST /_setup {"action":"add_node"...} -> Error
-POST /_setup?i_know_what_i_am_doing=true {"action":"add_node"...} -> Add node, stay in State 3.
-POST /_setup {"action":"delete_node"...} -> Stay in State 3, but delete node from /nodes, reflect the change in GET /_setup
-POST /_setup {"action":"delete_node","node":"unknown"} -> Error Unknown Node
+POST /_cluster_setup {"action":"enable_cluster"...} -> Error
+POST /_cluster_setup {"action":"finish_cluster"...} -> Stay in State 3, do nothing
+POST /_cluster_setup {"action":"add_node"...} -> Error
+POST /_cluster_setup?i_know_what_i_am_doing=true {"action":"add_node"...} -> Add node, stay in State 3.
+POST /_cluster_setup {"action":"delete_node"...} -> Stay in State 3, but delete node from /nodes, reflect the change in GET /_cluster_setup
+POST /_cluster_setup {"action":"delete_node","node":"unknown"} -> Error Unknown Node
```
// TBD: we need to persist the setup state somewhere.
diff --git a/src/setup.erl b/src/setup.erl
index 6657073ce..5a7100491 100644
--- a/src/setup.erl
+++ b/src/setup.erl
@@ -24,6 +24,11 @@ require_admins(undefined, {undefined, undefined}) ->
require_admins(_,_) ->
ok.
+require_node_count(undefined) ->
+ throw({error, "Cluster setup requires node_count to be configured"});
+require_node_count(_) ->
+ ok.
+
error_bind_address() ->
throw({error, "Cluster setup requires bind_addres != 127.0.0.1"}).
@@ -93,7 +98,8 @@ enable_cluster_http(Options) ->
{<<"username">>, AdminUsername},
{<<"password_hash">>, ?l2b(AdminPasswordHash)},
{<<"bind_address">>, couch_util:get_value(bind_address, Options)},
- {<<"port">>, couch_util:get_value(port, Options)}
+ {<<"port">>, couch_util:get_value(port, Options)},
+ {<<"node_count">>, couch_util:get_value(node_count, Options)}
]}),
Headers = [
@@ -147,6 +153,10 @@ enable_cluster_int(Options, no) ->
config:set("chttpd", "bind_address", binary_to_list(NewBindAddress))
end,
+ NodeCount = couch_util:get_value(node_count, Options),
+ ok = require_node_count(NodeCount),
+ config:set_integer("cluster", "n", NodeCount),
+
Port = proplists:get_value(port, Options),
case Port of
undefined ->
@@ -157,7 +167,6 @@ enable_cluster_int(Options, no) ->
config:set_integer("chttpd", "port", Port)
end,
couch_log:notice("Enable Cluster: ~p~n", [Options]).
- %cluster_state:set(enabled).
set_admin(Username, Password) ->
config:set("admins", binary_to_list(Username), binary_to_list(Password)).
@@ -199,13 +208,14 @@ add_node_int(Options, ok) ->
Host = proplists:get_value(host, Options),
Port = get_port(proplists:get_value(port, Options, 5984)),
+ Name = proplists:get_value(name, Options, get_default_name(Port)),
Url = binary_to_list(<<"http://", Host/binary, ":", Port/binary, "/_cluster_setup">>),
case ibrowse:send_req(Url, Headers, post, Body, RequestOptions) of
{ok, "201", _, _} ->
% when done, PUT :5986/nodes/nodeB
- create_node_doc(Host, Port);
+ create_node_doc(Host, Name);
Else ->
couch_log:notice("send_req: ~p~n", [Else]),
Else
@@ -218,16 +228,15 @@ get_port(Port) when is_list(Port) ->
get_port(Port) when is_binary(Port) ->
Port.
-create_node_doc(Host, Port) ->
+create_node_doc(Host, Name) ->
{ok, Db} = couch_db:open_int(<<"_nodes">>, []),
- Name = get_name(Port),
Doc = {[{<<"_id">>, <<Name/binary, "@", Host/binary>>}]},
Options = [],
CouchDoc = couch_doc:from_json_obj(Doc),
couch_db:update_doc(Db, CouchDoc, Options).
-get_name(Port) ->
+get_default_name(Port) ->
case Port of
% shortcut for easier development
<<"15984">> ->
diff --git a/src/setup_httpd.erl b/src/setup_httpd.erl
index 910fcb78d..a23a3e21d 100644
--- a/src/setup_httpd.erl
+++ b/src/setup_httpd.erl
@@ -62,7 +62,8 @@ handle_action("enable_cluster", Setup) ->
{port, <<"port">>},
{remote_node, <<"remote_node">>},
{remote_current_user, <<"remote_current_user">>},
- {remote_current_password, <<"remote_current_password">>}
+ {remote_current_password, <<"remote_current_password">>},
+ {node_count, <<"node_count">>}
], Setup),
case setup:enable_cluster(Options) of
{error, cluster_enabled} ->
@@ -88,7 +89,8 @@ handle_action("add_node", Setup) ->
{username, <<"username">>},
{password, <<"password">>},
{host, <<"host">>},
- {port, <<"port">>}
+ {port, <<"port">>},
+ {name, <<"name">>}
], Setup),
case setup:add_node(Options) of
{error, cluster_not_enabled} ->
diff --git a/test/t-frontend-setup.sh b/test/t-frontend-setup.sh
index 1c610b6cc..52056a374 100755
--- a/test/t-frontend-setup.sh
+++ b/test/t-frontend-setup.sh
@@ -16,13 +16,13 @@ HEADERS="-HContent-Type:application/json"
curl a:b@127.0.0.1:15986/_nodes/_all_docs
# Enable Cluster on node A
-curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"enable_cluster","username":"foo","password":"baz","bind_address":"0.0.0.0"}' $HEADERS
+curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"enable_cluster","username":"foo","password":"baz","bind_address":"0.0.0.0","node_count":2}' $HEADERS
# Enable Cluster on node B
-curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"enable_cluster","remote_node":"127.0.0.1","port":"25984","remote_current_user":"a","remote_current_password":"b","username":"foo","password":"baz","bind_address":"0.0.0.0"}' $HEADERS
+curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"enable_cluster","remote_node":"127.0.0.1","port":"25984","remote_current_user":"a","remote_current_password":"b","username":"foo","password":"baz","bind_address":"0.0.0.0","node_count":2}' $HEADERS
# Add node B on node A
-curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"add_node","username":"foo","password":"baz","host":"127.0.0.1","port":25984}' $HEADERS
+curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"add_node","username":"foo","password":"baz","host":"127.0.0.1","port":25984,"name":"node2"}' $HEADERS
# Show cluster state:
curl a:b@127.0.0.1:15986/_nodes/_all_docs
@@ -57,4 +57,7 @@ curl a:b@127.0.0.1:25984/_replicator
curl a:b@127.0.0.1:25984/_metadata
curl a:b@127.0.0.1:25984/_global_changes
+# Number of nodes is set to 2
+curl a:b@127.0.0.1:25984/_node/node2@127.0.0.1/_config/cluster/n
+
echo "YAY ALL GOOD"
diff --git a/test/t.sh b/test/t.sh
index 62abb61d7..6bd74cdd7 100755
--- a/test/t.sh
+++ b/test/t.sh
@@ -16,13 +16,13 @@ HEADERS="-HContent-Type:application/json"
curl a:b@127.0.0.1:15986/_nodes/_all_docs
# Enable Cluster on node A
-curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"enable_cluster","username":"foo","password":"baz","bind_address":"0.0.0.0"}' $HEADERS
+curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"enable_cluster","username":"foo","password":"baz","bind_address":"0.0.0.0","node_count":2}' $HEADERS
# Enable Cluster on node B
-curl a:b@127.0.0.1:25984/_cluster_setup -d '{"action":"enable_cluster","username":"foo","password":"baz","bind_address":"0.0.0.0"}' $HEADERS
+curl a:b@127.0.0.1:25984/_cluster_setup -d '{"action":"enable_cluster","username":"foo","password":"baz","bind_address":"0.0.0.0","node_count":2}' $HEADERS
# Add node B on node A
-curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"add_node","username":"foo","password":"baz","host":"127.0.0.1","port":25984}' $HEADERS
+curl a:b@127.0.0.1:15984/_cluster_setup -d '{"action":"add_node","username":"foo","password":"baz","host":"127.0.0.1","port":25984,"name":"node2"}' $HEADERS
# Show cluster state:
curl a:b@127.0.0.1:15986/_nodes/_all_docs
@@ -57,4 +57,7 @@ curl a:b@127.0.0.1:25984/_replicator
curl a:b@127.0.0.1:25984/_metadata
curl a:b@127.0.0.1:25984/_global_changes
+# Number of nodes is set to 2
+curl a:b@127.0.0.1:25984/_node/node2@127.0.0.1/_config/cluster/n
+
echo "YAY ALL GOOD"