diff options
Diffstat (limited to 'src/setup/README.md')
-rw-r--r-- | src/setup/README.md | 193 |
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. |