summaryrefslogtreecommitdiff
path: root/src/setup/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'src/setup/README.md')
-rw-r--r--src/setup/README.md193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/setup/README.md b/src/setup/README.md
new file mode 100644
index 000000000..e30c40027
--- /dev/null
+++ b/src/setup/README.md
@@ -0,0 +1,193 @@
+This module implements /_cluster_setup and manages the setting up, duh, of a CouchDB cluster.
+
+### Testing
+
+```bash
+git clone https://git-wip-us.apache.org/repos/asf/couchdb.git
+cd couchdb
+git checkout setup
+./configure
+make
+dev/run --no-join -n 2 --admin a:b
+```
+
+Then, in a new terminal:
+
+ $ src/setup/test/t.sh
+
+Before running each test, kill the `dev/run` script, then reset the
+CouchDB instances with:
+
+ $ rm -rf dev/lib/ dev/logs/
+ $ dev/run --no-join -n 2 --admin a:b
+
+before running the next shell script.
+
+The Plan:
+
+N. End User Action
+- What happens behind the scenes.
+
+
+1. Launch CouchDB with `$ couchdb`, or init.d, or any other way, exactly
+like it is done in 1.x.x.
+- CouchDB launches and listens on 127.0.0.1:5984
+
+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
+username, admin password and optionally a bind_address and port to bind
+to publicly. Submit the form with the [Enable Cluster] button.
+
+If this is a single node install that already has an admin set up, there
+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 a JSON entity to /_cluster_setup, the entity looks like:
+```
+{
+ "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 /_cluster_setup as shown above.
+
+Repeat on all nodes.
+- keep the same username/password everywhere.
+
+
+3. Pick any one node, for simplicity use the first one, to be the
+“setup coordination node”.
+- this is a “master” node that manages the setup and requires all
+ other nodes to be able to see it and vice versa. Setup won’t work
+ with unavailable nodes (duh). The notion of “master” will be gone
+ once the setup is finished. At that point, the system has no
+ master node. Ignore I ever said “master”.
+
+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 a JSON entity to /_cluster_setup, the entity looks like:
+```
+{
+ "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
+}
+```
+
+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
+ a UUID and set the erlang cookie to to that UUID.
+ - 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/_cluster_setup with:
+```
+ {
+ "action": "receive_cookie",
+ "cookie": "<secretcookie>",
+ }
+```
+
+ - when the request to node B returns, we know the Erlang-level
+ inter-cluster communication is enabled and we can start adding
+ the node on the CouchDB level. To do that, the “setup
+ coordination node” does this to it’s own HTTP endpoint:
+ PUT /nodes/node_b:5984 or the same thing with internal APIs.
+
+- Repeat for all nodes.
+- Fauxton keeps a list of all set up nodes for users to see.
+
+
+4.a. When all nodes are added, click the [Finish Cluster Setup] button
+in Fauxton.
+- this does POST /_cluster_setup
+```
+ {
+ "action": "finish_cluster"
+ }
+```
+
+b. Same as in a.
+
+- this manages the final setup bits, like creating the _users,
+ _replicator and _metadata, _db_updates endpoints and
+ whatever else is needed. // TBD: collect what else is needed.
+
+
+## The Setup Endpoint
+
+This is not a REST-y endpoint, it is a simple state machine operated
+by HTTP POST with JSON bodies that have an `action` field.
+
+### State 1: No Cluster Enabled
+
+This is right after starting a node for the first time, and any time
+before the cluster is enabled as outlined above.
+
+```
+GET /_cluster_setup
+{"state": "cluster_disabled"}
+
+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 /_cluster_setup
+{"state":"cluster_enabled","nodes":[]}
+
+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 /_cluster_setup
+{"state":"cluster_finished","nodes":["node a", "node b", ...]}
+
+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.