[Python-modules-commits] [python-digitalocean] 11/19: Tag unittest (#167)
Andrew Starr-Bochicchio
asb at moszumanska.debian.org
Tue Jun 20 00:31:29 UTC 2017
This is an automated email from the git hooks/post-receive script.
asb pushed a commit to annotated tag 1.10.1
in repository python-digitalocean.
commit dca2143ce6376f665c0f67125e1068b72e75a5c7
Author: moeseth <slamduck at gmail.com>
Date: Tue Oct 11 15:10:34 2016 +0630
Tag unittest (#167)
* Create Tag
* add to get_all_droplets filtering by tag_name
* Update and rename Tag to Tag.py
* Update Tag.py
* Small changes applied to refactor the code
* added tag unittests and possibly resolving #137
* fixes #140
* Initial support for creating multiple droplets with one API call - no ssh_keys option.
* Fixes after testing.
* Removed some debug output
* Add support for using ssh_keys.
* Use static instead of a class method.
* Added an example to help using the SSHKeys (Issue #144)
* Fixing #138 with a type mistake
* Ignoring pyc and __pycache__ when building docker container
* Using pytest as module (instead of the script)
* Add SSH key into account
* Add support for Block Storage volumes.
* Add a unit test for create_multiple.
* Version 1.9.0!
* Droplet: Add support for adding volumes at droplet creation time
* Don't pass --use-mirrors to pip
The --use-mirrors option was removed in pip 7.0.0.
This reverts commit f064571fecfd86a31f7d323206c4f453537b7056.
* added tag unittests and possibly resolving #137
* Small changes applied to refactor the code
* Create Tag
* add to get_all_droplets filtering by tag_name
* Update and rename Tag to Tag.py
* Update Tag.py
* fixes #140
---
README.md | 25 +++++-
digitalocean/Droplet.py | 4 +
digitalocean/Manager.py | 7 +-
digitalocean/Tag.py | 85 ++++++++++++-------
digitalocean/__init__.py | 1 +
digitalocean/baseapi.py | 11 ++-
digitalocean/tests/data/droplets/bytag.json | 104 ++++++++++++++++++++++++
digitalocean/tests/data/tags/resources.json | 8 ++
digitalocean/tests/data/tags/single.json | 11 +++
digitalocean/tests/data/tags/updatetag.json | 101 +++++++++++++++++++++++
digitalocean/tests/test_droplet.py | 1 +
digitalocean/tests/test_manager.py | 39 ++++++++-
digitalocean/tests/test_tag.py | 122 ++++++++++++++++++++++++++++
13 files changed, 482 insertions(+), 37 deletions(-)
diff --git a/README.md b/README.md
index d751ac0..3e8897f 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@ python-digitalocean support all the features provided via digitalocean.com APIs,
* Get public Images
* Get Droplet's event status
* Create and Remove a Droplet
+* Create, Add and Remove Tags from Droplets
* Resize a Droplet
* Shutdown, restart and boot a Droplet
* Power off, power on and "power cycle" a Droplet
@@ -43,9 +44,31 @@ This example shows how to list all the active droplets:
```python
import digitalocean
manager = digitalocean.Manager(token="secretspecialuniquesnowflake")
-print(manager.get_all_droplets())
+my_droplets = manager.get_all_droplets()
+print(my_droplets)
```
+### Listing the droplets by tags
+
+This example shows how to list all the active droplets:
+
+```python
+import digitalocean
+manager = digitalocean.Manager(token="secretspecialuniquesnowflake")
+my_droplets = manager.get_all_droplets(tag_name="awesome")
+print(my_droplets)
+```
+
+### Add a tag to a droplet
+
+This example shows how to list all the active droplets:
+
+```python
+import digitalocean
+tag = digitalocean.Tag(token="secretspecialuniquesnowflake", name="tag_name")
+tag.create() # create tag if not already created
+tag.add_droplets(["DROPLET_ID"])
+```
### Shutdown all droplets
diff --git a/digitalocean/Droplet.py b/digitalocean/Droplet.py
index 059d071..3b76836 100644
--- a/digitalocean/Droplet.py
+++ b/digitalocean/Droplet.py
@@ -89,6 +89,7 @@ class Droplet(BaseAPI):
self.private_networking = None
self.user_data = None
self.volumes = []
+ self.tags = []
# This will load also the values passed
super(Droplet, self).__init__(*args, **kwargs)
@@ -189,6 +190,9 @@ class Droplet(BaseAPI):
else:
self.private_networking = False
+ if "tags" in droplets:
+ self.tags = droplets["tags"]
+
return self
def _perform_action(self, params, return_dict=True):
diff --git a/digitalocean/Manager.py b/digitalocean/Manager.py
index c40dffd..e5e3c69 100644
--- a/digitalocean/Manager.py
+++ b/digitalocean/Manager.py
@@ -88,11 +88,12 @@ class Manager(BaseAPI):
"""
This function returns a list of Droplet object.
"""
- url = "droplets/"
if tag_name:
- url += '?tag_name=' + tag_name
+ params = {"tag_name": tag_name}
+ data = self.get_data("droplets/", params=params)
+ else:
+ data = self.get_data("droplets/")
- data = self.get_data(url)
droplets = list()
for jsoned in data['droplets']:
droplet = Droplet(**jsoned)
diff --git a/digitalocean/Tag.py b/digitalocean/Tag.py
index 88eddc5..b6722ba 100644
--- a/digitalocean/Tag.py
+++ b/digitalocean/Tag.py
@@ -1,19 +1,20 @@
from .baseapi import BaseAPI
from .Droplet import Droplet
-
class Tag(BaseAPI):
def __init__(self, *args, **kwargs):
self.name = ""
self.resources = {}
super(Tag, self).__init__(*args, **kwargs)
+
@classmethod
def get_object(cls, api_token, tag_name):
tag = cls(token=api_token, name=tag_name)
tag.load()
return tag
+
def load(self):
"""
Fetch data about tag
@@ -26,6 +27,7 @@ class Tag(BaseAPI):
return self
+
def create(self, **kwargs):
"""
Create the tag.
@@ -33,14 +35,27 @@ class Tag(BaseAPI):
for attr in kwargs.keys():
setattr(self, attr, kwargs[attr])
- query = {"name": self.name}
+ params = {"name": self.name}
- output = self.get_data("tags/", type="POST", params=query)
+ output = self.get_data("tags/", type="POST", params=params)
if output:
self.name = output['tag']['name']
self.resources = output['tag']['resources']
- def get_resources(self, resources, method):
+
+ def update_tag(self, name):
+ query = {"name": name}
+ updated = self.get_data("tags/%s" % self.name, type="PUT", params=query)
+ if updated:
+ self.name = updated["tag"]["name"]
+
+
+ def delete(self):
+ return self.get_data("tags/%s" % self.name, type="DELETE")
+
+
+ def __get_resources(self, resources, method):
+
""" Method used to talk directly to the API (TAGs' Resources) """
tagged = self.get_data(
'tags/%s/resources' % self.name, params={
@@ -50,23 +65,26 @@ class Tag(BaseAPI):
)
return tagged
- def add_resources(self, resources):
+
+ def __add_resources(self, resources):
"""
Add to the resources to this tag.
Attributes accepted at creation time:
resources: array - See API.
"""
- return self.get_resources(resources, method='POST')
+ return self.__get_resources(resources, method='POST')
- def remove_resources(self, resources):
+
+ def __remove_resources(self, resources):
"""
Remove resources from this tag.
Attributes accepted at creation time:
resources: array - See API.
"""
- return self.get_resources(resources, method='DELETE')
+ return self.__get_resources(resources, method='DELETE')
+
def __extract_resources_from_droplets(self, data):
"""
@@ -78,13 +96,17 @@ class Tag(BaseAPI):
if not isinstance(data, list): return data
for a_droplet in data:
res = {}
- if isinstance(a_droplet, str) or isinstance(a_droplet, int):
+
+ if isinstance(a_droplet, str) or isinstance(a_droplet, unicode) or isinstance(a_droplet, int):
res = {"resource_id": a_droplet, "resource_type": "droplet"}
elif isinstance(a_droplet, Droplet):
res = {"resource_id": a_droplet.id, "resource_type": "droplet"}
- else:
- continue
- resources.append(res)
+
+ if len(res) > 0:
+ resources.append(res)
+
+ return resources
+
def add_droplets(self, droplet):
"""
@@ -93,15 +115,17 @@ class Tag(BaseAPI):
Attributes accepted at creation time:
droplet: array of string or array of int, or array of Droplets.
"""
- if isinstance(droplet, list):
- # Extracting data from the Droplet object
- resources = self.__extract_resources_from_droplets(droplet)
- return self.add_resources(resources)
- else:
- return self.add_resources([{
- "resource_id": droplet.id,
- "resource_type": "droplet"
- }])
+ droplets = droplet
+ if not isinstance(droplets, list):
+ droplets = [droplet]
+
+ # Extracting data from the Droplet object
+ resources = self.__extract_resources_from_droplets(droplets)
+ if len(resources) > 0:
+ return self.__add_resources(resources)
+
+ return False
+
def remove_droplets(self, droplet):
"""
@@ -110,12 +134,13 @@ class Tag(BaseAPI):
Attributes accepted at creation time:
droplet: array of string or array of int, or array of Droplets.
"""
- if isinstance(droplet, list):
- # Extracting data from the Droplet object
- resources = self.__extract_resources_from_droplets(droplet)
- return self.remove_resources(resources)
- else:
- return self.remove_resources([{
- "resource_id": droplet.id,
- "resource_type": "droplet"
- }])
+ droplets = droplet
+ if not isinstance(droplets, list):
+ droplets = [droplet]
+
+ # Extracting data from the Droplet object
+ resources = self.__extract_resources_from_droplets(droplets)
+ if len(resources) > 0:
+ return self.__remove_resources(resources)
+
+ return False
diff --git a/digitalocean/__init__.py b/digitalocean/__init__.py
index 3d64cd0..babe283 100644
--- a/digitalocean/__init__.py
+++ b/digitalocean/__init__.py
@@ -21,3 +21,4 @@ from .Kernel import Kernel
from .FloatingIP import FloatingIP
from .Volume import Volume
from .baseapi import Error, TokenError, DataReadError
+from .Tag import Tag
diff --git a/digitalocean/baseapi.py b/digitalocean/baseapi.py
index a8b4db5..9576469 100644
--- a/digitalocean/baseapi.py
+++ b/digitalocean/baseapi.py
@@ -33,6 +33,10 @@ class JSONReadError(Error):
pass
+class NotFoundError(Error):
+ pass
+
+
class BaseAPI(object):
"""
Basic api class for
@@ -74,8 +78,8 @@ class BaseAPI(object):
PUT: (requests.put, {'Content-type': 'application/json'}, 'data',
json_dumps),
DELETE: (requests.delete,
- {'content-type': 'application/x-www-form-urlencoded'},
- 'params', identity),
+ {'content-type': 'application/json'},
+ 'data', json_dumps),
}
requests_method, headers, payload, transform = lookup[type]
@@ -114,6 +118,9 @@ class BaseAPI(object):
if req.status_code == 204:
return True
+ if req.status_code == 404:
+ raise NotFoundError()
+
try:
data = req.json()
except ValueError as e:
diff --git a/digitalocean/tests/data/droplets/bytag.json b/digitalocean/tests/data/droplets/bytag.json
new file mode 100644
index 0000000..91334ad
--- /dev/null
+++ b/digitalocean/tests/data/droplets/bytag.json
@@ -0,0 +1,104 @@
+{
+ "droplets": [
+ {
+ "id": 3164444,
+ "name": "example.com",
+ "memory": 512,
+ "vcpus": 1,
+ "disk": 20,
+ "locked": false,
+ "status": "active",
+ "kernel": {
+ "id": 2233,
+ "name": "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic",
+ "version": "3.13.0-37-generic"
+ },
+ "created_at": "2014-11-14T16:29:21Z",
+ "features": [
+ "backups",
+ "ipv6",
+ "virtio"
+ ],
+ "backup_ids": [
+ 7938002
+ ],
+ "snapshot_ids": [
+
+ ],
+ "image": {
+ "id": 6918990,
+ "name": "14.04 x64",
+ "distribution": "Ubuntu",
+ "slug": "ubuntu-14-04-x64",
+ "public": true,
+ "regions": [
+ "nyc1",
+ "ams1",
+ "sfo1",
+ "nyc2",
+ "ams2",
+ "sgp1",
+ "lon1",
+ "nyc3",
+ "ams3",
+ "nyc3"
+ ],
+ "created_at": "2014-10-17T20:24:33Z",
+ "type": "snapshot",
+ "min_disk_size": 20,
+ "size_gigabytes": 2.34
+ },
+ "volumes": [
+
+ ],
+ "size": {
+ },
+ "size_slug": "512mb",
+ "networks": {
+ "v4": [
+ {
+ "ip_address": "104.236.32.182",
+ "netmask": "255.255.192.0",
+ "gateway": "104.236.0.1",
+ "type": "public"
+ }
+ ],
+ "v6": [
+ {
+ "ip_address": "2604:A880:0800:0010:0000:0000:02DD:4001",
+ "netmask": 64,
+ "gateway": "2604:A880:0800:0010:0000:0000:0000:0001",
+ "type": "public"
+ }
+ ]
+ },
+ "region": {
+ "name": "New York 3",
+ "slug": "nyc3",
+ "sizes": [
+
+ ],
+ "features": [
+ "virtio",
+ "private_networking",
+ "backups",
+ "ipv6",
+ "metadata"
+ ],
+ "available": null
+ },
+ "tags": [
+ "awesome"
+ ]
+ }
+ ],
+ "links": {
+ "pages": {
+ "last": "https://api.digitalocean.com/v2/droplets?page=3&per_page=1",
+ "next": "https://api.digitalocean.com/v2/droplets?page=2&per_page=1"
+ }
+ },
+ "meta": {
+ "total": 3
+ }
+}
diff --git a/digitalocean/tests/data/tags/resources.json b/digitalocean/tests/data/tags/resources.json
new file mode 100644
index 0000000..8316d48
--- /dev/null
+++ b/digitalocean/tests/data/tags/resources.json
@@ -0,0 +1,8 @@
+{
+ "resources": [
+ {
+ "resource_id": "9569411",
+ "resource_type": "droplet"
+ }
+ ]
+}
diff --git a/digitalocean/tests/data/tags/single.json b/digitalocean/tests/data/tags/single.json
new file mode 100644
index 0000000..ed42cff
--- /dev/null
+++ b/digitalocean/tests/data/tags/single.json
@@ -0,0 +1,11 @@
+{
+ "tag": {
+ "name": "awesome",
+ "resources": {
+ "droplets": {
+ "count": 0,
+ "last_tagged": null
+ }
+ }
+ }
+}
diff --git a/digitalocean/tests/data/tags/updatetag.json b/digitalocean/tests/data/tags/updatetag.json
new file mode 100644
index 0000000..5866c94
--- /dev/null
+++ b/digitalocean/tests/data/tags/updatetag.json
@@ -0,0 +1,101 @@
+{
+ "tag": {
+ "name": "extra-awesome",
+ "resources": {
+ "droplets": {
+ "count": 1,
+ "last_tagged": {
+ "id": 3164444,
+ "name": "example.com",
+ "memory": 512,
+ "vcpus": 1,
+ "disk": 20,
+ "locked": false,
+ "status": "active",
+ "kernel": {
+ "id": 2233,
+ "name": "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic",
+ "version": "3.13.0-37-generic"
+ },
+ "created_at": "2014-11-14T16:29:21Z",
+ "features": [
+ "backups",
+ "ipv6",
+ "virtio"
+ ],
+ "backup_ids": [
+ 7938002
+ ],
+ "snapshot_ids": [
+
+ ],
+ "image": {
+ "id": 6918990,
+ "name": "14.04 x64",
+ "distribution": "Ubuntu",
+ "slug": "ubuntu-14-04-x64",
+ "public": true,
+ "regions": [
+ "nyc1",
+ "ams1",
+ "sfo1",
+ "nyc2",
+ "ams2",
+ "sgp1",
+ "lon1",
+ "nyc3",
+ "ams3",
+ "nyc3"
+ ],
+ "created_at": "2014-10-17T20:24:33Z",
+ "type": "snapshot",
+ "min_disk_size": 20,
+ "size_gigabytes": 2.34
+ },
+ "volumes": [
+
+ ],
+ "size": {
+ },
+ "size_slug": "512mb",
+ "networks": {
+ "v4": [
+ {
+ "ip_address": "104.236.32.182",
+ "netmask": "255.255.192.0",
+ "gateway": "104.236.0.1",
+ "type": "public"
+ }
+ ],
+ "v6": [
+ {
+ "ip_address": "2604:A880:0800:0010:0000:0000:02DD:4001",
+ "netmask": 64,
+ "gateway": "2604:A880:0800:0010:0000:0000:0000:0001",
+ "type": "public"
+ }
+ ]
+ },
+ "region": {
+ "name": "New York 3",
+ "slug": "nyc3",
+ "sizes": [
+
+ ],
+ "features": [
+ "virtio",
+ "private_networking",
+ "backups",
+ "ipv6",
+ "metadata"
+ ],
+ "available": null
+ },
+ "tags": [
+ "extra-awesome"
+ ]
+ }
+ }
+ }
+ }
+}
diff --git a/digitalocean/tests/test_droplet.py b/digitalocean/tests/test_droplet.py
index b7ce888..9931e8a 100644
--- a/digitalocean/tests/test_droplet.py
+++ b/digitalocean/tests/test_droplet.py
@@ -53,6 +53,7 @@ class TestDroplet(BaseTest):
"2604:A880:0800:0010:0000:0000:031D:2001")
self.assertEqual(d.kernel['id'], 2233)
self.assertEqual(d.features, ["ipv6", "virtio"])
+ self.assertEqual(d.tags, [])
@responses.activate
def test_power_off(self):
diff --git a/digitalocean/tests/test_manager.py b/digitalocean/tests/test_manager.py
index 63efa39..a5304f1 100644
--- a/digitalocean/tests/test_manager.py
+++ b/digitalocean/tests/test_manager.py
@@ -1,6 +1,5 @@
import unittest
import responses
-
import digitalocean
from .BaseTest import BaseTest
@@ -86,6 +85,44 @@ class TestManager(BaseTest):
"virtio"])
@responses.activate
+ def test_get_droplets_by_tag(self):
+ data = self.load_from_file('droplets/bytag.json')
+
+ url = self.base_url + 'droplets/'
+ responses.add(responses.GET, url,
+ body=data,
+ status=200,
+ content_type='application/json')
+
+ manager = digitalocean.Manager(token=self.token)
+ droplets = manager.get_all_droplets(tag_name="awesome")
+
+ droplet = droplets[0]
+ self.assertEqual(droplet.token, self.token)
+ self.assertEqual(droplet.id, 3164444)
+ self.assertEqual(droplet.name, "example.com")
+ self.assertEqual(droplet.memory, 512)
+ self.assertEqual(droplet.vcpus, 1)
+ self.assertEqual(droplet.disk, 20)
+ self.assertEqual(droplet.backups, True)
+ self.assertEqual(droplet.ipv6, True)
+ self.assertEqual(droplet.private_networking, False)
+ self.assertEqual(droplet.region['slug'], "nyc3")
+ self.assertEqual(droplet.status, "active")
+ self.assertEqual(droplet.image['slug'], "ubuntu-14-04-x64")
+ self.assertEqual(droplet.size_slug, '512mb')
+ self.assertEqual(droplet.created_at, "2014-11-14T16:29:21Z")
+ self.assertEqual(droplet.ip_address, "104.236.32.182")
+ self.assertEqual(droplet.ip_v6_address,
+ "2604:A880:0800:0010:0000:0000:02DD:4001")
+ self.assertEqual(droplet.kernel['id'], 2233)
+ self.assertEqual(droplet.backup_ids, [7938002])
+ self.assertEqual(droplet.features, ["backups",
+ "ipv6",
+ "virtio"])
+ self.assertEqual(droplet.tags, ["awesome"])
+
+ @responses.activate
def test_get_all_regions(self):
data = self.load_from_file('regions/all.json')
diff --git a/digitalocean/tests/test_tag.py b/digitalocean/tests/test_tag.py
new file mode 100644
index 0000000..ce0467f
--- /dev/null
+++ b/digitalocean/tests/test_tag.py
@@ -0,0 +1,122 @@
+import unittest
+import responses
+import digitalocean
+import json
+
+from .BaseTest import BaseTest
+
+
+class TestTags(BaseTest):
+
+ def setUp(self):
+ super(TestTags, self).setUp()
+
+ @responses.activate
+ def test_load(self):
+ data = self.load_from_file('tags/single.json')
+
+ responses.add(responses.GET,
+ self.base_url + "tags/awesome",
+ body=data,
+ status=200,
+ content_type='application/json')
+
+ droplet_tag = digitalocean.Tag(name='awesome', token=self.token)
+ droplet_tag.load()
+
+ self.assertEqual(responses.calls[0].request.url,
+ self.base_url + "tags/awesome")
+ self.assertEqual(droplet_tag.name,
+ "awesome")
+
+
+ @responses.activate
+ def test_create(self):
+ data = self.load_from_file('tags/single.json')
+
+ responses.add(responses.POST,
+ self.base_url + "tags/",
+ body=data,
+ status=201,
+ content_type='application/json')
+
+ droplet_tag = digitalocean.Tag(name='awesome', token=self.token)
+ droplet_tag.create()
+
+ self.assertEqual(responses.calls[0].request.url,
+ self.base_url + "tags/")
+ self.assertEqual(droplet_tag.name, "awesome")
+
+ @responses.activate
+ def test_update_tag(self):
+ data = self.load_from_file('tags/updatetag.json')
+
+ responses.add(responses.PUT,
+ self.base_url + "tags/awesome",
+ body=data,
+ status=200,
+ content_type='application/json')
+
+ droplet_tag = digitalocean.Tag(name='awesome', token=self.token)
+ droplet_tag.update_tag(name="extra-awesome")
+
+ self.assertEqual(responses.calls[0].request.url,
+ self.base_url + "tags/awesome")
+ self.assertEqual(droplet_tag.name, "extra-awesome")
+
+
+ @responses.activate
+ def test_delete(self):
+ responses.add(responses.DELETE,
+ self.base_url + "tags/awesome",
+ status=204,
+ content_type='application/json')
+
+ droplet_tag = digitalocean.Tag(name='awesome', token=self.token)
+ droplet_tag.delete()
+
+ self.assertEqual(responses.calls[0].request.url,
+ self.base_url + "tags/awesome")
+ self.assertEqual(droplet_tag.name, "awesome")
+
+
+ @responses.activate
+ def test_add_droplets(self):
+ data = self.load_from_file('tags/resources.json')
+
+ responses.add(responses.POST,
+ self.base_url + "tags/awesome/resources",
+ body=data,
+ status=204,
+ content_type='application/json')
+
+ resource_id = json.loads(data)["resources"][0]["resource_id"]
+
+ droplet_tag = digitalocean.Tag(name='awesome', token=self.token)
+ droplet_tag.add_droplets([resource_id])
+
+ self.assertEqual(responses.calls[0].request.url,
+ self.base_url + "tags/awesome/resources")
+
+
+ @responses.activate
+ def test_remove_droplets(self):
+ data = self.load_from_file('tags/resources.json')
+
+ responses.add(responses.DELETE,
+ self.base_url + "tags/awesome/resources",
+ body=data,
+ status=201,
+ content_type='application/json')
+
+ resource_id = json.loads(data)["resources"][0]["resource_id"]
+
+ droplet_tag = digitalocean.Tag(name='awesome', token=self.token)
+ droplet_tag.remove_droplets([resource_id])
+
+ self.assertEqual(responses.calls[0].request.url,
+ self.base_url + "tags/awesome/resources")
+
+
+if __name__ == '__main__':
+ unittest.main()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-digitalocean.git
More information about the Python-modules-commits
mailing list