Skip to content

Commit 027b56c

Browse files
support for multiple tokens (#309)
* baseapi: support for multiple tokens To exceed limits of API for single token add support for multiple tokens used in round-robin style. * pass multiple tokens to new instances * nitpicking * pass internally token list instead of single token Co-authored-by: Tomáš Čech <sleep_walker@gnu.org>
1 parent 629935c commit 027b56c

6 files changed

Lines changed: 58 additions & 39 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Dokcerfile made to test this
1+
# Dockerfile made to test this
22
FROM python:alpine
33

44
MAINTAINER Lorenzo Setale <lorenzo@setale.me>

digitalocean/Action.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
class Action(BaseAPI):
88
def __init__(self, *args, **kwargs):
99
self.id = None
10-
self.token = None
1110
self.status = None
1211
self.type = None
1312
self.started_at = None

digitalocean/Droplet.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def _perform_action(self, params, return_dict=True):
226226
return action
227227
else:
228228
action = action[u'action']
229-
return_action = Action(token=self.token)
229+
return_action = Action(token=self.tokens)
230230
# Loading attributes
231231
for attr in action.keys():
232232
setattr(return_action, attr, action[attr])
@@ -590,7 +590,7 @@ def get_actions(self):
590590
actions = []
591591
for action_dict in answer['actions']:
592592
action = Action(**action_dict)
593-
action.token = self.token
593+
action.token = self.tokens
594594
action.droplet_id = self.id
595595
action.load()
596596
actions.append(action)
@@ -603,7 +603,7 @@ def get_action(self, action_id):
603603
action_id (int): id of action
604604
"""
605605
return Action.get_object(
606-
api_token=self.token,
606+
api_token=self.tokens,
607607
action_id=action_id
608608
)
609609

@@ -616,7 +616,7 @@ def get_snapshots(self):
616616
for id in self.snapshot_ids:
617617
snapshot = Image()
618618
snapshot.id = id
619-
snapshot.token = self.token
619+
snapshot.token = self.tokens
620620
snapshots.append(snapshot)
621621
return snapshots
622622

@@ -630,7 +630,7 @@ def get_kernel_available(self):
630630
while True:
631631
for jsond in data[u'kernels']:
632632
kernel = Kernel(**jsond)
633-
kernel.token = self.token
633+
kernel.token = self.tokens
634634
kernels.append(kernel)
635635
try:
636636
url = data[u'links'][u'pages'].get(u'next')
@@ -653,7 +653,7 @@ def update_volumes_data(self):
653653
self.volumes = list()
654654

655655
for volume_id in self.volume_ids:
656-
volume = Volume().get_object(self.token, volume_id)
656+
volume = Volume().get_object(self.tokens, volume_id)
657657
self.volumes.append(volume)
658658

659659

digitalocean/Manager.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def get_account(self):
3434
"""
3535
Returns an Account object.
3636
"""
37-
return Account.get_object(api_token=self.token)
37+
return Account.get_object(api_token=self.tokens)
3838

3939
def get_balance(self):
4040
"""
@@ -50,7 +50,7 @@ def get_all_regions(self):
5050
regions = list()
5151
for jsoned in data['regions']:
5252
region = Region(**jsoned)
53-
region.token = self.token
53+
region.token = self.tokens
5454
regions.append(region)
5555
return regions
5656

@@ -69,7 +69,7 @@ def get_all_droplets(self, params=None, tag_name=None):
6969
droplets = list()
7070
for jsoned in data['droplets']:
7171
droplet = Droplet(**jsoned)
72-
droplet.token = self.token
72+
droplet.token = self.tokens
7373

7474
for net in droplet.networks['v4']:
7575
if net['type'] == 'private':
@@ -100,7 +100,7 @@ def get_droplet(self, droplet_id):
100100
"""
101101
Return a Droplet by its ID.
102102
"""
103-
return Droplet.get_object(api_token=self.token, droplet_id=droplet_id)
103+
return Droplet.get_object(api_token=self.tokens, droplet_id=droplet_id)
104104

105105
def get_all_sizes(self):
106106
"""
@@ -110,7 +110,7 @@ def get_all_sizes(self):
110110
sizes = list()
111111
for jsoned in data['sizes']:
112112
size = Size(**jsoned)
113-
size.token = self.token
113+
size.token = self.tokens
114114
sizes.append(size)
115115
return sizes
116116

@@ -127,7 +127,7 @@ def get_images(self, private=False, type=None):
127127
images = list()
128128
for jsoned in data['images']:
129129
image = Image(**jsoned)
130-
image.token = self.token
130+
image.token = self.tokens
131131
images.append(image)
132132
return images
133133

@@ -144,7 +144,7 @@ def get_image(self, image_id_or_slug):
144144
Return a Image by its ID/Slug.
145145
"""
146146
return Image.get_object(
147-
api_token=self.token,
147+
api_token=self.tokens,
148148
image_id_or_slug=image_id_or_slug,
149149
)
150150

@@ -166,7 +166,7 @@ def get_global_images(self):
166166
images = list()
167167
for i in data:
168168
if i.public:
169-
i.token = self.token
169+
i.token = self.tokens
170170
images.append(i)
171171
return images
172172

@@ -194,15 +194,15 @@ def get_all_domains(self):
194194
domains = list()
195195
for jsoned in data['domains']:
196196
domain = Domain(**jsoned)
197-
domain.token = self.token
197+
domain.token = self.tokens
198198
domains.append(domain)
199199
return domains
200200

201201
def get_domain(self, domain_name):
202202
"""
203203
Return a Domain by its domain_name
204204
"""
205-
return Domain.get_object(api_token=self.token, domain_name=domain_name)
205+
return Domain.get_object(api_token=self.tokens, domain_name=domain_name)
206206

207207
def get_all_sshkeys(self):
208208
"""
@@ -212,15 +212,15 @@ def get_all_sshkeys(self):
212212
ssh_keys = list()
213213
for jsoned in data['ssh_keys']:
214214
ssh_key = SSHKey(**jsoned)
215-
ssh_key.token = self.token
215+
ssh_key.token = self.tokens
216216
ssh_keys.append(ssh_key)
217217
return ssh_keys
218218

219219
def get_ssh_key(self, ssh_key_id):
220220
"""
221221
Return a SSHKey object by its ID.
222222
"""
223-
return SSHKey.get_object(api_token=self.token, ssh_key_id=ssh_key_id)
223+
return SSHKey.get_object(api_token=self.tokens, ssh_key_id=ssh_key_id)
224224

225225
def get_all_tags(self):
226226
"""
@@ -235,7 +235,7 @@ def get_action(self, action_id):
235235
"""
236236
Return an Action object by a specific ID.
237237
"""
238-
return Action.get_object(api_token=self.token, action_id=action_id)
238+
return Action.get_object(api_token=self.tokens, action_id=action_id)
239239

240240
def get_all_floating_ips(self):
241241
"""
@@ -245,15 +245,15 @@ def get_all_floating_ips(self):
245245
floating_ips = list()
246246
for jsoned in data['floating_ips']:
247247
floating_ip = FloatingIP(**jsoned)
248-
floating_ip.token = self.token
248+
floating_ip.token = self.tokens
249249
floating_ips.append(floating_ip)
250250
return floating_ips
251251

252252
def get_floating_ip(self, ip):
253253
"""
254254
Returns a of FloatingIP object by its IP address.
255255
"""
256-
return FloatingIP.get_object(api_token=self.token, ip=ip)
256+
return FloatingIP.get_object(api_token=self.tokens, ip=ip)
257257

258258
def get_all_load_balancers(self):
259259
"""
@@ -264,7 +264,7 @@ def get_all_load_balancers(self):
264264
load_balancers = list()
265265
for jsoned in data['load_balancers']:
266266
load_balancer = LoadBalancer(**jsoned)
267-
load_balancer.token = self.token
267+
load_balancer.token = self.tokens
268268
load_balancer.health_check = HealthCheck(**jsoned['health_check'])
269269
load_balancer.sticky_sessions = StickySessions(**jsoned['sticky_sessions'])
270270
forwarding_rules = list()
@@ -281,7 +281,7 @@ def get_load_balancer(self, id):
281281
Args:
282282
id (str): Load Balancer ID
283283
"""
284-
return LoadBalancer.get_object(api_token=self.token, id=id)
284+
return LoadBalancer.get_object(api_token=self.tokens, id=id)
285285

286286
def get_certificate(self, id):
287287
"""
@@ -290,7 +290,7 @@ def get_certificate(self, id):
290290
Args:
291291
id (str): Certificate ID
292292
"""
293-
return Certificate.get_object(api_token=self.token, cert_id=id)
293+
return Certificate.get_object(api_token=self.tokens, cert_id=id)
294294

295295
def get_all_certificates(self):
296296
"""
@@ -300,7 +300,7 @@ def get_all_certificates(self):
300300
certificates = list()
301301
for jsoned in data['certificates']:
302302
cert = Certificate(**jsoned)
303-
cert.token = self.token
303+
cert.token = self.tokens
304304
certificates.append(cert)
305305

306306
return certificates
@@ -310,7 +310,7 @@ def get_snapshot(self, snapshot_id):
310310
Return a Snapshot by its ID.
311311
"""
312312
return Snapshot.get_object(
313-
api_token=self.token, snapshot_id=snapshot_id
313+
api_token=self.tokens, snapshot_id=snapshot_id
314314
)
315315

316316
def get_all_snapshots(self):
@@ -319,7 +319,7 @@ def get_all_snapshots(self):
319319
"""
320320
data = self.get_data("snapshots/")
321321
return [
322-
Snapshot(token=self.token, **snapshot)
322+
Snapshot(token=self.tokens, **snapshot)
323323
for snapshot in data['snapshots']
324324
]
325325

@@ -329,7 +329,7 @@ def get_droplet_snapshots(self):
329329
"""
330330
data = self.get_data("snapshots?resource_type=droplet")
331331
return [
332-
Snapshot(token=self.token, **snapshot)
332+
Snapshot(token=self.tokens, **snapshot)
333333
for snapshot in data['snapshots']
334334
]
335335

@@ -339,7 +339,7 @@ def get_volume_snapshots(self):
339339
"""
340340
data = self.get_data("snapshots?resource_type=volume")
341341
return [
342-
Snapshot(token=self.token, **snapshot)
342+
Snapshot(token=self.tokens, **snapshot)
343343
for snapshot in data['snapshots']
344344
]
345345

@@ -355,15 +355,15 @@ def get_all_volumes(self, region=None):
355355
volumes = list()
356356
for jsoned in data['volumes']:
357357
volume = Volume(**jsoned)
358-
volume.token = self.token
358+
volume.token = self.tokens
359359
volumes.append(volume)
360360
return volumes
361361

362362
def get_volume(self, volume_id):
363363
"""
364364
Returns a Volume object by its ID.
365365
"""
366-
return Volume.get_object(api_token=self.token, volume_id=volume_id)
366+
return Volume.get_object(api_token=self.tokens, volume_id=volume_id)
367367

368368
def get_all_projects(self):
369369
"""
@@ -403,7 +403,7 @@ def get_all_firewalls(self):
403403
firewalls = list()
404404
for jsoned in data['firewalls']:
405405
firewall = Firewall(**jsoned)
406-
firewall.token = self.token
406+
firewall.token = self.tokens
407407
in_rules = list()
408408
for rule in jsoned['inbound_rules']:
409409
in_rules.append(InboundRule(**rule))
@@ -420,7 +420,7 @@ def get_firewall(self, firewall_id):
420420
Return a Firewall by its ID.
421421
"""
422422
return Firewall.get_object(
423-
api_token=self.token,
423+
api_token=self.tokens,
424424
firewall_id=firewall_id,
425425
)
426426

digitalocean/Volume.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def get_snapshots(self):
189189
snapshots = list()
190190
for jsond in data[u'snapshots']:
191191
snapshot = Snapshot(**jsond)
192-
snapshot.token = self.token
192+
snapshot.token = self.tokens
193193
snapshots.append(snapshot)
194194

195195
return snapshots

digitalocean/baseapi.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class BaseAPI(object):
4747
"""
4848
Basic api class for
4949
"""
50-
token = ""
50+
tokens = []
51+
_last_used = 0
5152
end_point = "https://api.digitalocean.com/v2/"
5253

5354
def __init__(self, *args, **kwargs):
@@ -59,7 +60,7 @@ def __init__(self, *args, **kwargs):
5960

6061
for attr in kwargs.keys():
6162
setattr(self, attr, kwargs[attr])
62-
63+
6364
parsed_url = urlparse.urlparse(self.end_point)
6465
if not parsed_url.scheme or not parsed_url.netloc:
6566
raise EndPointError("Provided end point is not a valid URL. Please use a valid URL")
@@ -123,7 +124,9 @@ def __perform_request(self, url, type=GET, params=None):
123124
kwargs['timeout'] = timeout
124125

125126
# remove token from log
126-
headers_str = str(headers).replace(self.token.strip(), 'TOKEN')
127+
headers_str = str(headers)
128+
for i, token in enumerate(self.tokens):
129+
headers_str = headers_str.replace(token.strip(), 'TOKEN%s' % i)
127130
self._log.debug('%s %s %s:%s %s %s' %
128131
(type, url, payload, params, headers_str, timeout))
129132

@@ -162,6 +165,23 @@ def __init_ratelimit(self, headers):
162165
# Add the account requests limit reset time
163166
self.ratelimit_reset = headers.get('Ratelimit-Reset', None)
164167

168+
@property
169+
def token(self):
170+
# use all the tokens round-robin style
171+
if self.tokens:
172+
self._last_used = (self._last_used + 1) % len(self.tokens)
173+
return self.tokens[self._last_used]
174+
return ""
175+
176+
@token.setter
177+
def token(self, token):
178+
self._last_used = 0
179+
if isinstance(token, list):
180+
self.tokens = token
181+
else:
182+
# for backward compatibility
183+
self.tokens = [token]
184+
165185
def get_timeout(self):
166186
"""
167187
Checks if any timeout for the requests to DigitalOcean is required.

0 commit comments

Comments
 (0)