Skip to content

Commit fe2e317

Browse files
leo-naekadennisv
authored andcommitted
Use swiftclient.Connection to handle operation retries natively (#93)
1 parent 7a3f924 commit fe2e317

File tree

4 files changed

+113
-89
lines changed

4 files changed

+113
-89
lines changed

swift/storage.py

Lines changed: 32 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class SwiftStorage(Storage):
145145
_token = ''
146146
name_prefix = setting('SWIFT_NAME_PREFIX', '')
147147
full_listing = setting('SWIFT_FULL_LISTING', True)
148+
max_retries = setting('SWIFT_MAX_RETRIES', 5)
148149

149150
def __init__(self, **settings):
150151
# check if some of the settings provided as class attributes
@@ -169,21 +170,19 @@ def __init__(self, **settings):
169170
}
170171
self.os_options.update(self.os_extra_options)
171172

172-
# Get authentication token
173-
self.storage_url, self.token = swiftclient.get_auth(
174-
self.api_auth_url,
175-
self.api_username,
176-
self.api_key,
177-
auth_version=self.auth_version,
178-
os_options=self.os_options)
179-
self.http_conn = swiftclient.http_connection(self.storage_url)
173+
# Get Connection wrapper
174+
self.swift_conn = swiftclient.Connection(
175+
authurl=self.api_auth_url,
176+
user=self.api_username,
177+
key=self.api_key,
178+
retries=self.max_retries,
179+
tenant_name=self.tenant_name,
180+
os_options=self.os_options,
181+
auth_version=self.auth_version)
180182

181183
# Check container
182184
try:
183-
swiftclient.head_container(self.storage_url,
184-
self.token,
185-
self.container_name,
186-
http_conn=self.http_conn)
185+
self.swift_conn.head_container(self.container_name)
187186
except swiftclient.ClientException:
188187
headers = {}
189188
if self.auto_create_container:
@@ -192,11 +191,8 @@ def __init__(self, **settings):
192191
if self.auto_create_container_allow_orgin:
193192
headers['X-Container-Meta-Access-Control-Allow-Origin'] = \
194193
self.auto_create_container_allow_orgin
195-
swiftclient.put_container(self.storage_url,
196-
self.token,
197-
self.container_name,
198-
http_conn=self.http_conn,
199-
headers=headers)
194+
self.swift_conn.put_container(self.container_name,
195+
headers=headers)
200196
else:
201197
raise ImproperlyConfigured(
202198
"Container %s does not exist." % self.container_name)
@@ -205,7 +201,7 @@ def __init__(self, **settings):
205201
# Derive a base URL based on the authentication information from
206202
# the server, optionally overriding the protocol, host/port and
207203
# potentially adding a path fragment before the auth information.
208-
self.base_url = self.storage_url + '/'
204+
self.base_url = self.swift_conn.url + '/'
209205
if self.override_base_url is not None:
210206
# override the protocol and host, append any path fragments
211207
split_derived = urlparse.urlsplit(self.base_url)
@@ -222,32 +218,11 @@ def __init__(self, **settings):
222218
else:
223219
self.base_url = self.override_base_url
224220

225-
def get_token(self):
226-
if time() - self._token_creation_time >= self.auth_token_duration:
227-
new_token = swiftclient.get_auth(
228-
self.api_auth_url,
229-
self.api_username,
230-
self.api_key,
231-
auth_version=self.auth_version,
232-
os_options=self.os_options)[1]
233-
self.token = new_token
234-
return self._token
235-
236-
def set_token(self, new_token):
237-
self._token_creation_time = time()
238-
self._token = new_token
239-
240-
token = property(get_token, set_token)
241-
242221
def _open(self, name, mode='rb'):
243222
original_name = name
244223
name = self.name_prefix + name
245224

246-
headers, content = swiftclient.get_object(self.storage_url,
247-
self.token,
248-
self.container_name,
249-
name,
250-
http_conn=self.http_conn)
225+
headers, content = self.swift_conn.get_object(self.container_name, name)
251226
buf = BytesIO(content)
252227
buf.name = os.path.basename(original_name)
253228
buf.mode = mode
@@ -273,15 +248,12 @@ def _save(self, name, content, headers=None):
273248
else:
274249
content_type = mimetypes.guess_type(name)[0]
275250
content_length = content.size
276-
swiftclient.put_object(self.storage_url,
277-
self.token,
278-
self.container_name,
279-
name,
280-
content,
281-
http_conn=self.http_conn,
282-
content_type=content_type,
283-
content_length=content_length,
284-
headers=headers)
251+
self.swift_conn.put_object(self.container_name,
252+
name,
253+
content,
254+
content_length=content_length,
255+
content_type=content_type,
256+
headers=headers)
285257
return original_name
286258

287259
def get_headers(self, name):
@@ -294,12 +266,8 @@ def get_headers(self, name):
294266
"""
295267
if name != self.last_headers_name:
296268
# miss -> update
297-
self.last_headers_value = swiftclient.head_object(
298-
self.storage_url,
299-
self.token,
300-
self.container_name,
301-
name,
302-
http_conn=self.http_conn)
269+
self.last_headers_value = self.swift_conn.head_object(
270+
self.container_name, name)
303271
self.last_headers_name = name
304272
return self.last_headers_value
305273

@@ -314,11 +282,7 @@ def exists(self, name):
314282
@prepend_name_prefix
315283
def delete(self, name):
316284
try:
317-
swiftclient.delete_object(self.storage_url,
318-
self.token,
319-
self.container_name,
320-
name,
321-
http_conn=self.http_conn)
285+
self.swift_conn.delete_object(self.container_name, name)
322286
except swiftclient.ClientException:
323287
pass
324288

@@ -386,9 +350,8 @@ def isdir(self, name):
386350

387351
@prepend_name_prefix
388352
def listdir(self, path):
389-
container = swiftclient.get_container(self.storage_url, self.token,
390-
self.container_name, prefix=path,
391-
full_listing=self.full_listing)
353+
container = self.swift_conn.get_container(
354+
self.container_name, prefix=path, full_listing=self.full_listing)
392355
files = []
393356
dirs = []
394357
for obj in container[1]:
@@ -404,23 +367,18 @@ def listdir(self, path):
404367

405368
@prepend_name_prefix
406369
def makedirs(self, dirs):
407-
swiftclient.put_object(self.storage_url,
408-
token=self.token,
409-
container=self.container_name,
410-
name='%s/.' % (self.name_prefix + dirs),
411-
contents='')
370+
self.swift_conn.put_object(self.container_name,
371+
'%s/.' % (self.name_prefix + dirs),
372+
contents='')
412373

413374
@prepend_name_prefix
414375
def rmtree(self, abs_path):
415-
container = swiftclient.get_container(self.storage_url, self.token,
416-
self.container_name)
376+
container = self.swift_conn.get_container(self.container_name)
417377

418378
for obj in container[1]:
419379
if obj['name'].startswith(abs_path):
420-
swiftclient.delete_object(self.storage_url,
421-
token=self.token,
422-
container=self.container_name,
423-
name=obj['name'])
380+
self.swift_conn.delete_object(self.container_name,
381+
obj['name'])
424382

425383

426384
class StaticSwiftStorage(SwiftStorage):

tests/tests.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,18 +160,18 @@ def test_illegal_extra_opts(self):
160160
self.default_storage('v3', os_extra_options="boom!")
161161

162162

163-
@patch('swift.storage.swiftclient', new=FakeSwift)
164-
class TokenTest(SwiftStorageTestCase):
165-
166-
def test_get_token(self):
167-
"""Renewing token"""
168-
backend = self.default_storage('v3', auth_token_duration=0)
169-
backend.get_token()
170-
171-
def test_set_token(self):
172-
"""Set token manually"""
173-
backend = self.default_storage('v3', auth_token_duration=0)
174-
backend.set_token('token')
163+
# @patch('swift.storage.swiftclient', new=FakeSwift)
164+
# class TokenTest(SwiftStorageTestCase):
165+
166+
# def test_get_token(self):
167+
# """Renewing token"""
168+
# backend = self.default_storage('v3', auth_token_duration=0)
169+
# backend.get_token()
170+
171+
# def test_set_token(self):
172+
# """Set token manually"""
173+
# backend = self.default_storage('v3', auth_token_duration=0)
174+
# backend.set_token('token')
175175

176176

177177
@patch('swift.storage.swiftclient', new=FakeSwift)

tests/utils.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,71 @@ class FakeSwift(object):
9090
objects = CONTAINER_CONTENTS
9191
containers = ['container']
9292

93+
class Connection(object):
94+
def __init__(self, authurl=None, user=None, key=None, retries=5,
95+
preauthurl=None, preauthtoken=None, snet=False,
96+
starting_backoff=1, max_backoff=64, tenant_name=None,
97+
os_options=None, auth_version="1", cacert=None,
98+
insecure=False, cert=None, cert_key=None,
99+
ssl_compression=True, retry_on_ratelimit=False,
100+
timeout=None, session=None):
101+
self.service_token = None
102+
103+
def _retry(self, reset_func, func, *args, **kwargs):
104+
self.url, self.token = self.get_auth()
105+
self.http_conn = None
106+
return func(self.url, self.token, *args,
107+
service_token=self.service_token, **kwargs)
108+
109+
def get_auth(self):
110+
return base_url(), TOKEN
111+
112+
def head_container(self, container, headers=None):
113+
return self._retry(None, FakeSwift.head_container, container,
114+
headers=headers)
115+
116+
def put_container(self, container, headers=None, response_dict=None,
117+
query_string=None):
118+
return self._retry(None, FakeSwift.put_container, container,
119+
headers=headers, response_dict=response_dict,
120+
query_string=query_string)
121+
122+
def head_object(self, container, obj, headers=None):
123+
return self._retry(None, FakeSwift.head_object, container, obj,
124+
headers=headers)
125+
126+
def get_object(self, container, obj, resp_chunk_size=None,
127+
query_string=None, response_dict=None, headers=None):
128+
return self._retry(None, FakeSwift.get_object, container, obj,
129+
resp_chunk_size=resp_chunk_size,
130+
query_string=query_string,
131+
response_dict=response_dict, headers=headers)
132+
133+
def get_container(self, container, marker=None, limit=None, prefix=None,
134+
delimiter=None, end_marker=None, path=None,
135+
full_listing=False, headers=None, query_string=None):
136+
return self._retry(None, FakeSwift.get_container, container,
137+
marker=marker, limit=limit, prefix=prefix,
138+
delimiter=delimiter, end_marker=end_marker,
139+
path=path, full_listing=full_listing,
140+
headers=headers, query_string=query_string)
141+
142+
def delete_object(self, container, obj, query_string=None,
143+
response_dict=None, headers=None):
144+
return self._retry(None, FakeSwift.delete_object, container, obj,
145+
query_string=query_string,
146+
response_dict=response_dict, headers=headers)
147+
148+
def put_object(self, container, obj, contents, content_length=None,
149+
etag=None, chunk_size=None, content_type=None,
150+
headers=None, query_string=None, response_dict=None):
151+
return self._retry(None, FakeSwift.put_object, container, obj,
152+
contents, content_length=content_length,
153+
etag=etag, chunk_size=chunk_size,
154+
content_type=content_type, headers=headers,
155+
query_string=query_string,
156+
response_dict=response_dict)
157+
93158
@classmethod
94159
def get_auth(cls, auth_url, user, passwd, **kwargs):
95160
return base_url(), TOKEN
@@ -138,7 +203,7 @@ def delete_object(cls, url, token, container, name, **kwargs):
138203
@classmethod
139204
def put_object(cls, url, token, container, name=None, contents=None,
140205
http_conn=None, content_type=None, content_length=None,
141-
headers=None):
206+
headers=None, **kwargs):
142207
if not name:
143208
raise ValueError("Attempting to add an object with no name/path")
144209
FakeSwift.objects.append(create_object(name))

tox.ini

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ args_are_paths = false
44
envlist =
55
flake8,
66
isort,
7-
py27-{1.9,1.10,master}
8-
py{34,35}-{1.9,1.10,master}
7+
py27-{1.9,1.10}
8+
py34-{1.9,1.10}
9+
py35-{1.9,1.10,master}
910

1011
[testenv]
1112
usedevelop = true

0 commit comments

Comments
 (0)