Skip to content
This repository has been archived by the owner on Feb 2, 2018. It is now read-only.

Commit

Permalink
Extend cluster endpoint to manage host membership
Browse files Browse the repository at this point in the history
This makes three major changes:

1) The cluster record in etcd now includes a 'hostset' list member
   containing the member hosts for the cluster.

2) New endpoints for querying and changing member hosts in a cluster.

   This can be done all at once:

     /api/v0/cluster/{NAME}/hosts

   Or individually:

     /api/v0/cluster/{NAME}/hosts/{IP}

   (see doc/endpoints.rst for further details)

3) Note that with this change, hosts must now be explicitly added
   to clusters.  However, host creation will soon support a means
   of specifying cluster membership.
  • Loading branch information
mbarnes authored and ashcrow committed Feb 5, 2016
1 parent 1316b44 commit e2633aa
Show file tree
Hide file tree
Showing 8 changed files with 634 additions and 34 deletions.
69 changes: 69 additions & 0 deletions doc/endpoints.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,75 @@ No body.



Cluster Members
---------------
**Endpoint**: /api/v0/cluster/{NAME}/hosts

GET
```
Retrieve the host list for a cluster.

.. code-block:: javascript
[
host_address,...
]
Example
~~~~~~~

.. code-block:: javascript
[
"192.168.100.50",
"192.168.100.51"
]
PUT
```
Replace the host list for a cluster. The "old" list must match the
current host list.

.. code-block:: javascript
{
"old": [host_address,...]
"new": [host_address,...]
}
Example
~~~~~~~

.. code-block:: javascript
{
"old": ["192.168.100.50"],
"new": ["192.168.100.50", "192.168.100.51"]
}
Cluster Members (Individual)
----------------------------
**Endpoint**: /api/v0/cluster/{NAME}/hosts/{IP}

GET
```
Membership test. Returns 200 if host {IP} is in cluster, else 404.

PUT
```
Adds host {IP} to cluster. (Idempotent)

No body.

DELETE
``````
Removes host {IP} from cluster. (Idempotent)

No body.



Cluster Operations: Upgrade
---------------------------
**Endpoint**: /api/v0/cluster/{NAME}/upgrade
Expand Down
105 changes: 99 additions & 6 deletions example/rest_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,22 +142,115 @@ def expected_status(r, code):
print(r.json())
expected_status(r, 201)

print("=> Examining Cluster 'honeynut' (All Hosts Implicitly Added)")
print("=> Examining Cluster 'honeynut' (No Hosts)")
r = requests.get(SERVER + '/api/v0/cluster/honeynut', auth=AUTH)
print(r.json())
expect = {'status': 'ok', 'hosts': {'total': 0, 'available': 0, 'unavailable': 0}}
expected_json(r.json(), expect) and expected_status(r, 200)

print("=> Verify Host List for Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH)
print(r.json())
expected_json(r.json(), []) and expected_status(r, 200)

print("=> Verify Host 10.2.0.2 Not In Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 404)

print("=> Adding Host 10.2.0.2 to Cluster 'honeynut'")
r = requests.put(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 200)

print("=> Examining Cluster 'honeynut' (1 Host)")
r = requests.get(SERVER + '/api/v0/cluster/honeynut', auth=AUTH)
print(r.json())
expect = {'status': 'ok', 'hosts': {'total': 1, 'available': 0, 'unavailable': 1}}
expected_json(r.json(), expect) and expected_status(r, 200)

# FIXME: Verify membership of host in cluster,
# once clusters have explicit members.
print("=> Verify Host List for Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH)
print(r.json())
expected_json(r.json(), ['10.2.0.2']) and expected_status(r, 200)

print("=> Deleting Host 10.2.0.2")
print("=> Verify Host 10.2.0.2 In Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 200)

print("=> Deleting Host 10.2.0.2 from Cluster 'honeynut'")
r = requests.delete(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 200)

print("=> Examining Cluster 'honeynut' (No Hosts)")
r = requests.get(SERVER + '/api/v0/cluster/honeynut', auth=AUTH)
print(r.json())
expect = {'status': 'ok', 'hosts': {'total': 0, 'available': 0, 'unavailable': 0}}
expected_json(r.json(), expect) and expected_status(r, 200)

print("=> Verify Host List for Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH)
print(r.json())
expected_json(r.json(), []) and expected_status(r, 200)

print("=> Verify Host 10.2.0.2 Not In Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 404)

print("=> Directly Set Host List for Cluster 'honeynut' (w/ Malformed Request)")
r = requests.put(
SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH,
json='Part of this nutritious breakfast!')
print(r.json())
expected_status(r, 400)

print("=> Directly Set Host List for Cluster 'honeynut' (w/ Wrong Prev Value)")
r = requests.put(
SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH,
json={"old": ["bogus"], "new": ["10.2.0.2"]})
print(r.json())
expected_status(r, 409)

print("=> Verify Host 10.2.0.2 Not In Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 404)

print("=> Directly Set Host List for Cluster 'honeynut'")
r = requests.put(
SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH,
json={"old": [], "new": ["10.2.0.2"]})
print(r.json())
expected_status(r, 200)

print("=> Verify Host 10.2.0.2 In Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 200)

print("=> Deleting Host 10.2.0.2 (Implicitly Deleted From Cluster)")
r = requests.delete(SERVER + '/api/v0/host/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 410)

# FIXME: Verify no remaining hosts in cluster,
# once clusters have explicit members.
print("=> Examining Cluster 'honeynut' (No Hosts)")
r = requests.get(SERVER + '/api/v0/cluster/honeynut', auth=AUTH)
print(r.json())
expect = {'status': 'ok', 'hosts': {'total': 0, 'available': 0, 'unavailable': 0}}
expected_json(r.json(), expect) and expected_status(r, 200)

print("=> Verify Host List for Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts', auth=AUTH)
print(r.json())
expected_json(r.json(), []) and expected_status(r, 200)

print("=> Verify Host 10.2.0.2 Not In Cluster 'honeynut'")
r = requests.get(SERVER + '/api/v0/cluster/honeynut/hosts/10.2.0.2', auth=AUTH)
print(r.json())
expected_status(r, 404)

print("=> Initiate Cluster Upgrade Without Auth (Should Fail)")
r = requests.put(
Expand Down

0 comments on commit e2633aa

Please sign in to comment.