Author: tomaz
Date: Wed Aug 8 04:34:45 2012
New Revision: 1370664
URL: http://svn.apache.org/viewvc?rev=1370664&view=rev
Log:
Add the following new methods to the CloudFiles driver:
ex_set_account_metadata_temp_url_key, ex_get_object_temp_url. Contributed by
Shawn Smith, part of GITHUB-72.
Modified:
libcloud/trunk/CHANGES
libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
libcloud/trunk/libcloud/test/storage/test_cloudfiles.py
Modified: libcloud/trunk/CHANGES
URL:
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1370664&r1=1370663&r2=1370664&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Wed Aug 8 04:34:45 2012
@@ -7,6 +7,12 @@ Changes with Apache Libcloud in developm
- Add new Rackspace Nova driver for Chicago (ORD) location ; LIBCLOUD-234
[Brian McDaniel]
+ *) Storage
+
+ - Add the following new methods to the CloudFiles driver:
+ ex_set_account_metadata_temp_url_key, ex_get_object_temp_url. ; GITHUB-72
+ [Shawn Smith]
+
Changes with Apache Libcloud 0.11.1:
*) General
Modified: libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py?rev=1370664&r1=1370663&r2=1370664&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/cloudfiles.py (original)
+++ libcloud/trunk/libcloud/storage/drivers/cloudfiles.py Wed Aug 8 04:34:45
2012
@@ -13,9 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from hashlib import sha1
+import hmac
import os
+from time import time
from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import urlencode
try:
import simplejson as json
@@ -407,10 +411,13 @@ class CloudFilesStorageDriver(StorageDri
'x-account-object-count', 'unknown')
bytes_used = response.headers.get(
'x-account-bytes-used', 'unknown')
+ temp_url_key = response.headers.get(
+ 'x-account-meta-temp-url-key', None)
return { 'container_count': int(container_count),
'object_count': int(object_count),
- 'bytes_used': int(bytes_used) }
+ 'bytes_used': int(bytes_used),
+ 'temp_url_key': temp_url_key }
raise LibcloudError('Unexpected status code: %s' % (response.status))
@@ -472,6 +479,63 @@ class CloudFilesStorageDriver(StorageDri
return response.status in [ httplib.CREATED, httplib.ACCEPTED ]
+ def ex_set_account_metadata_temp_url_key(self, key):
+ """
+ Set the metadata header X-Account-Meta-Temp-URL-Key on your Cloud
+ Files account.
+
+ @param key: X-Account-Meta-Temp-URL-Key
+ @type key: C{str}
+ """
+ headers = {'X-Account-Meta-Temp-URL-Key': key}
+
+ response = self.connection.request('',
+ method='POST',
+ headers=headers,
+ cdn_request=False)
+
+ return response.status in [httplib.OK, httplib.NO_CONTENT,
+ httplib.CREATED, httplib.ACCEPTED]
+
+ def ex_get_object_temp_url(self, obj, method='GET', timeout=60):
+ """
+ Create a temporary URL to allow others to retrieve or put objects
+ in your Cloud Files account for as long or as short a time as you
+ wish. This method is specifically for allowing users to retrieve
+ or update an object.
+
+ @param object: The object that you wish to make temporarily public
+ @type container: C{Object}
+ @param method: Which method you would like to allow, 'PUT' or 'GET'
+ @type method: C{str}
+ @param timeout: Time (in seconds) after which you want the TempURL
+ to expire.
+ @type timeout: C{int}
+ """
+ self.connection._populate_hosts_and_request_paths()
+ expires = int(time() + timeout)
+ path = '%s/%s/%s' % (self.connection.request_path,
+ obj.container.name, obj.name)
+ try:
+ key = self.ex_get_meta_data()['temp_url_key']
+ assert key is not None
+ except Exception:
+ raise KeyError('You must first set the ' +
+ 'X-Account-Meta-Temp-URL-Key header on your ' +
+ 'Cloud Files account using ' +
+ 'ex_set_account_metadata_temp_url_key before ' +
+ 'you can use this method.')
+ hmac_body = '%s\n%s\n%s' % (method, expires, path)
+ sig = hmac.new(b(key), b(hmac_body), sha1).hexdigest()
+ params = urlencode({'temp_url_sig': sig,
+ 'temp_url_expires': expires})
+
+ temp_url = 'https://%s/%s/%s?%s' % \
+ (self.connection.host + self.connection.request_path,
+ obj.container.name, obj.name, params)
+
+ return temp_url
+
def _upload_object_part(self, container, object_name, part_number,
iterator, verify_hash=True):
Modified: libcloud/trunk/libcloud/test/storage/test_cloudfiles.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/storage/test_cloudfiles.py?rev=1370664&r1=1370663&r2=1370664&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/storage/test_cloudfiles.py (original)
+++ libcloud/trunk/libcloud/test/storage/test_cloudfiles.py Wed Aug 8 04:34:45
2012
@@ -12,6 +12,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+from hashlib import sha1
+import hmac
import os
import os.path # pylint: disable-msg=W0404
import math
@@ -493,6 +495,7 @@ class CloudFilesTests(unittest.TestCase)
self.assertTrue('object_count' in meta_data)
self.assertTrue('container_count' in meta_data)
self.assertTrue('bytes_used' in meta_data)
+ self.assertTrue('temp_url_key' in meta_data)
@mock.patch('os.path.getsize')
def test_ex_multipart_upload_object_for_small_files(self, getsize_mock):
@@ -615,6 +618,42 @@ class CloudFilesTests(unittest.TestCase)
file_name='error.html')
self.assertTrue(result)
+ def test_ex_set_account_metadata_temp_url_key(self):
+ result = self.driver.ex_set_account_metadata_temp_url_key("a key")
+ self.assertTrue(result)
+
+ @mock.patch("libcloud.storage.drivers.cloudfiles.time")
+ def test_ex_get_object_temp_url(self, time):
+ time.return_value = 0
+ self.driver.ex_get_meta_data = mock.Mock()
+ self.driver.ex_get_meta_data.return_value = {'container_count': 1,
+ 'object_count': 1,
+ 'bytes_used': 1,
+ 'temp_url_key': 'foo'}
+ container = Container(name='foo_bar_container', extra={}, driver=self)
+ obj = Object(name='foo_bar_object', size=1000, hash=None, extra={},
+ container=container, meta_data=None,
+ driver=self)
+ hmac_body = "%s\n%s\n%s" % ('GET', 60,
+
"/v1/MossoCloudFS/foo_bar_container/foo_bar_object")
+ sig = hmac.new(b('foo'), b(hmac_body), sha1).hexdigest()
+ ret = self.driver.ex_get_object_temp_url(obj, 'GET')
+ temp_url =
'https://storage101.ord1.clouddrive.com/v1/MossoCloudFS/foo_bar_container/foo_bar_object?temp_url_expires=60&temp_url_sig=%s'
% (sig)
+
+ self.assertEquals(ret, temp_url)
+
+ def test_ex_get_object_temp_url_no_key_raises_key_error(self):
+ self.driver.ex_get_meta_data = mock.Mock()
+ self.driver.ex_get_meta_data.return_value = {'container_count': 1,
+ 'object_count': 1,
+ 'bytes_used': 1,
+ 'temp_url_key': None}
+ container = Container(name='foo_bar_container', extra={}, driver=self)
+ obj = Object(name='foo_bar_object', size=1000, hash=None, extra={},
+ container=container, meta_data=None,
+ driver=self)
+ self.assertRaises(KeyError, self.driver.ex_get_object_temp_url, obj,
'GET')
+
def _remove_test_file(self):
file_path = os.path.abspath(__file__) + '.temp'
@@ -674,6 +713,9 @@ class CloudFilesMockHttp(StorageMockHttp
'x-account-object-count': 400,
'x-account-bytes-used': 1234567
})
+ elif method == 'POST':
+ body = ''
+ status_code = httplib.NO_CONTENT
return (status_code, body, headers, httplib.responses[httplib.OK])
def _v1_MossoCloudFS_not_found(self, method, url, body, headers):