Some functional tests for static large objects
There's some sort-of-hacky code in there to detect SLO support in order to skip tests when SLO is off so that the functests won't fail on older clusters. Change-Id: I6ad5974a0db7213747b0f4497d08ffc706d3f220
This commit is contained in:
parent
8255810e7d
commit
2e1fc7446f
@ -604,7 +604,7 @@ class File(Base):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def read(self, size=-1, offset=0, hdrs=None, buffer=None,
|
def read(self, size=-1, offset=0, hdrs=None, buffer=None,
|
||||||
callback=None, cfg={}):
|
callback=None, cfg={}, parms={}):
|
||||||
|
|
||||||
if size > 0:
|
if size > 0:
|
||||||
range_string = 'bytes=%d-%d' % (offset, (offset + size) - 1)
|
range_string = 'bytes=%d-%d' % (offset, (offset + size) - 1)
|
||||||
@ -614,7 +614,7 @@ class File(Base):
|
|||||||
hdrs = {'Range': range_string}
|
hdrs = {'Range': range_string}
|
||||||
|
|
||||||
status = self.conn.make_request('GET', self.path, hdrs=hdrs,
|
status = self.conn.make_request('GET', self.path, hdrs=hdrs,
|
||||||
cfg=cfg)
|
cfg=cfg, parms=parms)
|
||||||
|
|
||||||
if(status < 200) or (status > 299):
|
if(status < 200) or (status > 299):
|
||||||
raise ResponseError(self.conn.response)
|
raise ResponseError(self.conn.response)
|
||||||
@ -734,6 +734,10 @@ class File(Base):
|
|||||||
(self.conn.response.status > 299):
|
(self.conn.response.status > 299):
|
||||||
raise ResponseError(self.conn.response)
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data.seek(0)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
self.md5 = self.compute_md5sum(data)
|
self.md5 = self.compute_md5sum(data)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
import locale
|
import locale
|
||||||
import random
|
import random
|
||||||
import StringIO
|
import StringIO
|
||||||
@ -884,7 +886,7 @@ class TestFile(Base):
|
|||||||
set_up = False
|
set_up = False
|
||||||
|
|
||||||
def testCopy(self):
|
def testCopy(self):
|
||||||
# makes sure to test encoded characters"
|
# makes sure to test encoded characters
|
||||||
source_filename = 'dealde%2Fl04 011e%204c8df/flash.png'
|
source_filename = 'dealde%2Fl04 011e%204c8df/flash.png'
|
||||||
file_item = self.env.container.file(source_filename)
|
file_item = self.env.container.file(source_filename)
|
||||||
|
|
||||||
@ -1624,5 +1626,228 @@ class TestFileComparison(Base):
|
|||||||
class TestFileComparisonUTF8(Base2, TestFileComparison):
|
class TestFileComparisonUTF8(Base2, TestFileComparison):
|
||||||
set_up = False
|
set_up = False
|
||||||
|
|
||||||
|
|
||||||
|
class TestSloEnv(object):
|
||||||
|
slo_enabled = None # tri-state: None initially, then True/False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUp(cls):
|
||||||
|
cls.conn = Connection(config)
|
||||||
|
cls.conn.authenticate()
|
||||||
|
cls.account = Account(cls.conn, config.get('account',
|
||||||
|
config['username']))
|
||||||
|
cls.account.delete_containers()
|
||||||
|
|
||||||
|
cls.container = cls.account.container(Utils.create_name())
|
||||||
|
|
||||||
|
if not cls.container.create():
|
||||||
|
raise ResponseError(cls.conn.response)
|
||||||
|
|
||||||
|
# TODO(seriously, anyone can do this): make this use the /info API once
|
||||||
|
# it lands, both for detection of SLO and for minimum segment size
|
||||||
|
if cls.slo_enabled is None:
|
||||||
|
test_file = cls.container.file(".test-slo")
|
||||||
|
try:
|
||||||
|
# If SLO is enabled, this'll raise an error since
|
||||||
|
# X-Static-Large-Object is a reserved header.
|
||||||
|
#
|
||||||
|
# If SLO is not enabled, then this will get the usual 2xx
|
||||||
|
# response.
|
||||||
|
test_file.write(
|
||||||
|
"some contents",
|
||||||
|
hdrs={'X-Static-Large-Object': 'true'})
|
||||||
|
except ResponseError as err:
|
||||||
|
if err.status == 400:
|
||||||
|
cls.slo_enabled = True
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
cls.slo_enabled = False
|
||||||
|
return
|
||||||
|
|
||||||
|
seg_info = {}
|
||||||
|
for letter, size in (('a', 1024 * 1024),
|
||||||
|
('b', 1024 * 1024),
|
||||||
|
('c', 1024 * 1024),
|
||||||
|
('d', 1024 * 1024),
|
||||||
|
('e', 1)):
|
||||||
|
seg_name = "seg_%s" % letter
|
||||||
|
file_item = cls.container.file(seg_name)
|
||||||
|
file_item.write(letter * size)
|
||||||
|
seg_info[seg_name] = {
|
||||||
|
'size_bytes': size,
|
||||||
|
'etag': file_item.md5,
|
||||||
|
'path': '/%s/%s' % (cls.container.name, seg_name)}
|
||||||
|
|
||||||
|
file_item = cls.container.file("manifest-abcde")
|
||||||
|
file_item.write(
|
||||||
|
json.dumps([seg_info['seg_a'], seg_info['seg_b'],
|
||||||
|
seg_info['seg_c'], seg_info['seg_d'],
|
||||||
|
seg_info['seg_e']]),
|
||||||
|
parms={'multipart-manifest': 'put'})
|
||||||
|
|
||||||
|
file_item = cls.container.file('manifest-cd')
|
||||||
|
cd_json = json.dumps([seg_info['seg_c'], seg_info['seg_d']])
|
||||||
|
file_item.write(cd_json, parms={'multipart-manifest': 'put'})
|
||||||
|
cd_etag = hashlib.md5(seg_info['seg_c']['etag'] +
|
||||||
|
seg_info['seg_d']['etag']).hexdigest()
|
||||||
|
|
||||||
|
file_item = cls.container.file("manifest-bcd-submanifest")
|
||||||
|
file_item.write(
|
||||||
|
json.dumps([seg_info['seg_b'],
|
||||||
|
{'etag': cd_etag,
|
||||||
|
'size_bytes': (seg_info['seg_c']['size_bytes'] +
|
||||||
|
seg_info['seg_d']['size_bytes']),
|
||||||
|
'path': '/%s/%s' % (cls.container.name,
|
||||||
|
'manifest-cd')}]),
|
||||||
|
parms={'multipart-manifest': 'put'})
|
||||||
|
bcd_submanifest_etag = hashlib.md5(
|
||||||
|
seg_info['seg_b']['etag'] + cd_etag).hexdigest()
|
||||||
|
|
||||||
|
file_item = cls.container.file("manifest-abcde-submanifest")
|
||||||
|
file_item.write(
|
||||||
|
json.dumps([
|
||||||
|
seg_info['seg_a'],
|
||||||
|
{'etag': bcd_submanifest_etag,
|
||||||
|
'size_bytes': (seg_info['seg_b']['size_bytes'] +
|
||||||
|
seg_info['seg_c']['size_bytes'] +
|
||||||
|
seg_info['seg_d']['size_bytes']),
|
||||||
|
'path': '/%s/%s' % (cls.container.name,
|
||||||
|
'manifest-bcd-submanifest')},
|
||||||
|
seg_info['seg_e']]),
|
||||||
|
parms={'multipart-manifest': 'put'})
|
||||||
|
|
||||||
|
|
||||||
|
class TestSlo(Base):
|
||||||
|
env = TestSloEnv
|
||||||
|
set_up = False
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestSlo, self).setUp()
|
||||||
|
if self.env.slo_enabled is False:
|
||||||
|
raise SkipTest("SLO not enabled")
|
||||||
|
elif self.env.slo_enabled is not True:
|
||||||
|
# just some sanity checking
|
||||||
|
raise Exception(
|
||||||
|
"Expected slo_enabled to be True/False, got %r" %
|
||||||
|
(self.env.slo_enabled,))
|
||||||
|
|
||||||
|
def test_slo_get_simple_manifest(self):
|
||||||
|
file_item = self.env.container.file('manifest-abcde')
|
||||||
|
file_contents = file_item.read()
|
||||||
|
self.assertEqual(4 * 1024 * 1024 + 1, len(file_contents))
|
||||||
|
self.assertEqual('a', file_contents[0])
|
||||||
|
self.assertEqual('a', file_contents[1024 * 1024 - 1])
|
||||||
|
self.assertEqual('b', file_contents[1024 * 1024])
|
||||||
|
self.assertEqual('d', file_contents[-2])
|
||||||
|
self.assertEqual('e', file_contents[-1])
|
||||||
|
|
||||||
|
def test_slo_get_nested_manifest(self):
|
||||||
|
file_item = self.env.container.file('manifest-abcde-submanifest')
|
||||||
|
file_contents = file_item.read()
|
||||||
|
self.assertEqual(4 * 1024 * 1024 + 1, len(file_contents))
|
||||||
|
self.assertEqual('a', file_contents[0])
|
||||||
|
self.assertEqual('a', file_contents[1024 * 1024 - 1])
|
||||||
|
self.assertEqual('b', file_contents[1024 * 1024])
|
||||||
|
self.assertEqual('d', file_contents[-2])
|
||||||
|
self.assertEqual('e', file_contents[-1])
|
||||||
|
|
||||||
|
def test_slo_ranged_get(self):
|
||||||
|
file_item = self.env.container.file('manifest-abcde')
|
||||||
|
file_contents = file_item.read(size=1024 * 1024 + 2,
|
||||||
|
offset=1024 * 1024 - 1)
|
||||||
|
self.assertEqual('a', file_contents[0])
|
||||||
|
self.assertEqual('b', file_contents[1])
|
||||||
|
self.assertEqual('b', file_contents[-2])
|
||||||
|
self.assertEqual('c', file_contents[-1])
|
||||||
|
|
||||||
|
def test_slo_ranged_submanifest(self):
|
||||||
|
file_item = self.env.container.file('manifest-abcde-submanifest')
|
||||||
|
file_contents = file_item.read(size=1024 * 1024 + 2,
|
||||||
|
offset=1024 * 1024 * 2 - 1)
|
||||||
|
self.assertEqual('b', file_contents[0])
|
||||||
|
self.assertEqual('c', file_contents[1])
|
||||||
|
self.assertEqual('c', file_contents[-2])
|
||||||
|
self.assertEqual('d', file_contents[-1])
|
||||||
|
|
||||||
|
def test_slo_etag_is_hash_of_etags(self):
|
||||||
|
expected_hash = hashlib.md5()
|
||||||
|
expected_hash.update(hashlib.md5('a' * 1024 * 1024).hexdigest())
|
||||||
|
expected_hash.update(hashlib.md5('b' * 1024 * 1024).hexdigest())
|
||||||
|
expected_hash.update(hashlib.md5('c' * 1024 * 1024).hexdigest())
|
||||||
|
expected_hash.update(hashlib.md5('d' * 1024 * 1024).hexdigest())
|
||||||
|
expected_hash.update(hashlib.md5('e').hexdigest())
|
||||||
|
expected_etag = expected_hash.hexdigest()
|
||||||
|
|
||||||
|
file_item = self.env.container.file('manifest-abcde')
|
||||||
|
self.assertEqual(expected_etag, file_item.info()['etag'])
|
||||||
|
|
||||||
|
def test_slo_etag_is_hash_of_etags_submanifests(self):
|
||||||
|
|
||||||
|
def hd(x):
|
||||||
|
return hashlib.md5(x).hexdigest()
|
||||||
|
|
||||||
|
expected_etag = hd(hd('a' * 1024 * 1024) +
|
||||||
|
hd(hd('b' * 1024 * 1024) +
|
||||||
|
hd(hd('c' * 1024 * 1024) +
|
||||||
|
hd('d' * 1024 * 1024))) +
|
||||||
|
hd('e'))
|
||||||
|
|
||||||
|
file_item = self.env.container.file('manifest-abcde-submanifest')
|
||||||
|
self.assertEqual(expected_etag, file_item.info()['etag'])
|
||||||
|
|
||||||
|
def test_slo_etag_mismatch(self):
|
||||||
|
file_item = self.env.container.file("manifest-a-bad-etag")
|
||||||
|
try:
|
||||||
|
file_item.write(
|
||||||
|
json.dumps([{
|
||||||
|
'size_bytes': 1024 * 1024,
|
||||||
|
'etag': 'not it',
|
||||||
|
'path': '/%s/%s' % (self.env.container.name, 'seg_a')}]),
|
||||||
|
parms={'multipart-manifest': 'put'})
|
||||||
|
except ResponseError as err:
|
||||||
|
self.assertEqual(400, err.status)
|
||||||
|
else:
|
||||||
|
self.fail("Expected ResponseError but didn't get it")
|
||||||
|
|
||||||
|
def test_slo_size_mismatch(self):
|
||||||
|
file_item = self.env.container.file("manifest-a-bad-size")
|
||||||
|
try:
|
||||||
|
file_item.write(
|
||||||
|
json.dumps([{
|
||||||
|
'size_bytes': 1024 * 1024 - 1,
|
||||||
|
'etag': hashlib.md5('a' * 1024 * 1024).hexdigest(),
|
||||||
|
'path': '/%s/%s' % (self.env.container.name, 'seg_a')}]),
|
||||||
|
parms={'multipart-manifest': 'put'})
|
||||||
|
except ResponseError as err:
|
||||||
|
self.assertEqual(400, err.status)
|
||||||
|
else:
|
||||||
|
self.fail("Expected ResponseError but didn't get it")
|
||||||
|
|
||||||
|
def test_slo_copy(self):
|
||||||
|
file_item = self.env.container.file("manifest-abcde")
|
||||||
|
file_item.copy(self.env.container.name, "copied-abcde")
|
||||||
|
|
||||||
|
copied = self.env.container.file("copied-abcde")
|
||||||
|
copied_contents = copied.read(parms={'multipart-manifest': 'get'})
|
||||||
|
self.assertEqual(4 * 1024 * 1024 + 1, len(copied_contents))
|
||||||
|
|
||||||
|
def test_slo_copy_the_manifest(self):
|
||||||
|
file_item = self.env.container.file("manifest-abcde")
|
||||||
|
file_item.copy(self.env.container.name, "copied-abcde",
|
||||||
|
parms={'multipart-manifest': 'get'})
|
||||||
|
|
||||||
|
copied = self.env.container.file("copied-abcde")
|
||||||
|
copied_contents = copied.read(parms={'multipart-manifest': 'get'})
|
||||||
|
try:
|
||||||
|
json.loads(copied_contents)
|
||||||
|
except ValueError:
|
||||||
|
self.fail("COPY didn't copy the manifest (invalid json on GET)")
|
||||||
|
|
||||||
|
|
||||||
|
class TestSloUTF8(Base2, TestSlo):
|
||||||
|
set_up = False
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user