Merge remote-tracking branch 'adrienverge/COUCHDB-3119'

* adrienverge/COUCHDB-3119:
  add_node: Don't fail if node name != "couchdb" or "node1"
diff --git a/README.md b/README.md
index 7316b25..a6c6d18 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,14 @@
 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 @@
 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,24 +81,21 @@
 
 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],
-      "name": "node1"  // as in “node1@hostname”, same as in vm.args
-    }
-  }
+{
+  "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
@@ -100,7 +103,7 @@
     - 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",
@@ -120,10 +123,10 @@
 
 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"
   }
 ```
 
@@ -145,41 +148,41 @@
 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 144c2c3..acbeb2d 100644
--- a/src/setup.erl
+++ b/src/setup.erl
@@ -24,6 +24,11 @@
 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"}).
 
@@ -46,7 +51,7 @@
 
 
 cluster_system_dbs() ->
-    ["_users", "_replicator", "_metadata", "_global_changes"].
+    ["_users", "_replicator", "_global_changes"].
 
 
 has_cluster_system_dbs() ->
@@ -91,7 +96,8 @@
         {<<"username">>, couch_util:get_value(username, Options)},
         {<<"password">>, couch_util:get_value(password, Options)},
         {<<"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 = [
@@ -142,6 +148,10 @@
             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 ->
@@ -152,7 +162,6 @@
             config:set_integer("chttpd", "port", Port)
     end,
     couch_log:notice("Enable Cluster: ~p~n", [Options]).
-    %cluster_state:set(enabled).
 
 maybe_set_admin(Username, Password) ->
     case couch_auth_cache:get_admin(Username) of
diff --git a/src/setup_httpd.erl b/src/setup_httpd.erl
index 59c9bf8..73ce147 100644
--- a/src/setup_httpd.erl
+++ b/src/setup_httpd.erl
@@ -61,7 +61,8 @@
         {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} ->
diff --git a/test/t-frontend-setup.sh b/test/t-frontend-setup.sh
index 7888925..52056a3 100755
--- a/test/t-frontend-setup.sh
+++ b/test/t-frontend-setup.sh
@@ -16,10 +16,10 @@
 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,"name":"node2"}' $HEADERS
@@ -57,4 +57,7 @@
 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 4fbe7ea..6bd74cd 100755
--- a/test/t.sh
+++ b/test/t.sh
@@ -16,10 +16,10 @@
 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,"name":"node2"}' $HEADERS
@@ -57,4 +57,7 @@
 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"