test_object_store_service.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import filecmp
  2. import os
  3. import tempfile
  4. import uuid
  5. from datetime import datetime
  6. from io import BytesIO
  7. from test import helpers
  8. from test.helpers import ProviderTestBase
  9. from test.helpers import standard_interface_tests as sit
  10. from unittest import skip
  11. from cloudbridge.cloud.factory import ProviderList
  12. from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
  13. from cloudbridge.cloud.interfaces.provider import TestMockHelperMixin
  14. from cloudbridge.cloud.interfaces.resources import Bucket
  15. from cloudbridge.cloud.interfaces.resources import BucketObject
  16. import requests
  17. class CloudObjectStoreServiceTestCase(ProviderTestBase):
  18. _multiprocess_can_split_ = True
  19. @helpers.skipIfNoService(['storage.buckets'])
  20. def test_crud_bucket(self):
  21. """
  22. Create a new bucket, check whether the expected values are set,
  23. and delete it.
  24. """
  25. def create_bucket(name):
  26. return self.provider.storage.buckets.create(name)
  27. def cleanup_bucket(bucket):
  28. bucket.delete()
  29. with self.assertRaises(InvalidNameException):
  30. # underscores are not allowed in bucket names
  31. create_bucket("cb_bucket")
  32. with self.assertRaises(InvalidNameException):
  33. # names of length less than 3 should raise an exception
  34. create_bucket("cb")
  35. with self.assertRaises(InvalidNameException):
  36. # names of length greater than 63 should raise an exception
  37. create_bucket("a" * 64)
  38. with self.assertRaises(InvalidNameException):
  39. # bucket name cannot be an IP address
  40. create_bucket("197.10.100.42")
  41. sit.check_crud(self, self.provider.storage.buckets, Bucket,
  42. "cb-crudbucket", create_bucket, cleanup_bucket,
  43. skip_name_check=True)
  44. @helpers.skipIfNoService(['storage.buckets'])
  45. def test_crud_bucket_object(self):
  46. test_bucket = None
  47. def create_bucket_obj(name):
  48. obj = test_bucket.objects.create(name)
  49. # TODO: This is wrong. We shouldn't have to have a separate
  50. # call to upload some content before being able to delete
  51. # the content. Maybe the create_object method should accept
  52. # the file content as a parameter.
  53. obj.upload("dummy content")
  54. return obj
  55. def cleanup_bucket_obj(bucket_obj):
  56. bucket_obj.delete()
  57. with helpers.cleanup_action(lambda: test_bucket.delete()):
  58. name = "cb-crudbucketobj-{0}".format(uuid.uuid4())
  59. test_bucket = self.provider.storage.buckets.create(name)
  60. sit.check_crud(self, test_bucket.objects, BucketObject,
  61. "cb-bucketobj", create_bucket_obj,
  62. cleanup_bucket_obj, skip_name_check=True)
  63. @helpers.skipIfNoService(['storage.buckets'])
  64. def test_crud_bucket_object_properties(self):
  65. """
  66. Create a new bucket, upload some contents into the bucket, and
  67. check whether list properly detects the new content.
  68. Delete everything afterwards.
  69. """
  70. name = "cbtestbucketobjs-{0}".format(uuid.uuid4())
  71. test_bucket = self.provider.storage.buckets.create(name)
  72. # ensure that the bucket is empty
  73. objects = test_bucket.objects.list()
  74. self.assertEqual([], objects)
  75. with helpers.cleanup_action(lambda: test_bucket.delete()):
  76. obj_name_prefix = "hello"
  77. obj_name = obj_name_prefix + "_world.txt"
  78. obj = test_bucket.objects.create(obj_name)
  79. with helpers.cleanup_action(lambda: obj.delete()):
  80. # TODO: This is wrong. We shouldn't have to have a separate
  81. # call to upload some content before being able to delete
  82. # the content. Maybe the create_object method should accept
  83. # the file content as a parameter.
  84. obj.upload("dummy content")
  85. objs = test_bucket.objects.list()
  86. self.assertTrue(
  87. isinstance(objs[0].size, int),
  88. "Object size property needs to be a int, not {0}".format(
  89. type(objs[0].size)))
  90. self.assertTrue(
  91. datetime.strptime(objs[0].last_modified[:23],
  92. "%Y-%m-%dT%H:%M:%S.%f"),
  93. "Object's last_modified field format {0} not matching."
  94. .format(objs[0].last_modified))
  95. # check iteration
  96. iter_objs = list(test_bucket.objects)
  97. self.assertListEqual(iter_objs, objs)
  98. obj_too = test_bucket.objects.get(obj_name)
  99. self.assertTrue(
  100. isinstance(obj_too, BucketObject),
  101. "Did not get object {0} of expected type.".format(obj_too))
  102. prefix_filtered_list = test_bucket.objects.list(
  103. prefix=obj_name_prefix)
  104. self.assertTrue(
  105. len(objs) == len(prefix_filtered_list) == 1,
  106. 'The number of objects returned by list function, '
  107. 'with and without a prefix, are expected to be equal, '
  108. 'but its detected otherwise.')
  109. sit.check_delete(self, test_bucket.objects, obj)
  110. @helpers.skipIfNoService(['storage.buckets'])
  111. def test_upload_download_bucket_content(self):
  112. name = "cbtestbucketobjs-{0}".format(uuid.uuid4())
  113. test_bucket = self.provider.storage.buckets.create(name)
  114. with helpers.cleanup_action(lambda: test_bucket.delete()):
  115. obj_name = "hello_upload_download.txt"
  116. obj = test_bucket.objects.create(obj_name)
  117. with helpers.cleanup_action(lambda: obj.delete()):
  118. content = b"Hello World. Here's some content."
  119. # TODO: Upload and download methods accept different parameter
  120. # types. Need to make this consistent - possibly provider
  121. # multiple methods like upload_from_file, from_stream etc.
  122. obj.upload(content)
  123. target_stream = BytesIO()
  124. obj.save_content(target_stream)
  125. self.assertEqual(target_stream.getvalue(), content)
  126. target_stream2 = BytesIO()
  127. for data in obj.iter_content():
  128. target_stream2.write(data)
  129. self.assertEqual(target_stream2.getvalue(), content)
  130. @helpers.skipIfNoService(['storage.buckets'])
  131. def test_generate_url(self):
  132. if self.provider.PROVIDER_ID == ProviderList.OPENSTACK:
  133. raise self.skipTest("Skip until OpenStack impl is provided")
  134. name = "cbtestbucketobjs-{0}".format(uuid.uuid4())
  135. test_bucket = self.provider.storage.buckets.create(name)
  136. with helpers.cleanup_action(lambda: test_bucket.delete()):
  137. obj_name = "hello_upload_download.txt"
  138. obj = test_bucket.objects.create(obj_name)
  139. with helpers.cleanup_action(lambda: obj.delete()):
  140. content = b"Hello World. Generate a url."
  141. obj.upload(content)
  142. target_stream = BytesIO()
  143. obj.save_content(target_stream)
  144. url = obj.generate_url(100)
  145. if isinstance(self.provider, TestMockHelperMixin):
  146. raise self.skipTest(
  147. "Skipping rest of test - mock providers can't"
  148. " access generated url")
  149. self.assertEqual(requests.get(url).content, content)
  150. @helpers.skipIfNoService(['storage.buckets'])
  151. def test_upload_download_bucket_content_from_file(self):
  152. name = "cbtestbucketobjs-{0}".format(uuid.uuid4())
  153. test_bucket = self.provider.storage.buckets.create(name)
  154. with helpers.cleanup_action(lambda: test_bucket.delete()):
  155. obj_name = "hello_upload_download.txt"
  156. obj = test_bucket.objects.create(obj_name)
  157. with helpers.cleanup_action(lambda: obj.delete()):
  158. test_file = os.path.join(
  159. helpers.get_test_fixtures_folder(), 'logo.jpg')
  160. obj.upload_from_file(test_file)
  161. target_stream = BytesIO()
  162. obj.save_content(target_stream)
  163. with open(test_file, 'rb') as f:
  164. self.assertEqual(target_stream.getvalue(), f.read())
  165. @skip("Skip unless you want to test swift objects bigger than 5 Gig")
  166. @helpers.skipIfNoService(['storage.buckets'])
  167. def test_upload_download_bucket_content_with_large_file(self):
  168. """
  169. Creates a 6 Gig file in the temp directory, then uploads it to
  170. Swift. Once uploaded, then downloads to a new file in the temp
  171. directory and compares the two files to see if they match.
  172. """
  173. temp_dir = tempfile.gettempdir()
  174. file_name = '6GigTest.tmp'
  175. six_gig_file = os.path.join(temp_dir, file_name)
  176. with open(six_gig_file, "wb") as out:
  177. out.truncate(6 * 1024 * 1024 * 1024) # 6 Gig...
  178. with helpers.cleanup_action(lambda: os.remove(six_gig_file)):
  179. download_file = "{0}/cbtestfile-{1}".format(temp_dir, file_name)
  180. bucket_name = "cbtestbucketlargeobjs-{0}".format(uuid.uuid4())
  181. test_bucket = self.provider.storage.buckets.create(bucket_name)
  182. with helpers.cleanup_action(lambda: test_bucket.delete()):
  183. test_obj = test_bucket.objects.create(file_name)
  184. with helpers.cleanup_action(lambda: test_obj.delete()):
  185. file_uploaded = test_obj.upload_from_file(six_gig_file)
  186. self.assertTrue(file_uploaded, "Could not upload object?")
  187. with helpers.cleanup_action(
  188. lambda: os.remove(download_file)):
  189. with open(download_file, 'wb') as f:
  190. test_obj.save_content(f)
  191. self.assertTrue(
  192. filecmp.cmp(six_gig_file, download_file),
  193. "Uploaded file != downloaded")