Add ability to pass data to create_object

create_object currently only accepts data in files. That's not awesome
for some usecases, such as creating 0-byte 'directory' nodes. Add a data
parameter so that data can be passed in.

Change-Id: I35e08d7b1a4fd6ded822edeba9e62a1390a6c4e8
This commit is contained in:
Monty Taylor 2018-07-19 12:53:03 -05:00
parent 5f46fe8842
commit 6ef2b1fffb
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
4 changed files with 95 additions and 7 deletions

View File

@ -7421,7 +7421,7 @@ class OpenStackCloud(_normalize.Normalizer):
self, container, name, filename=None,
md5=None, sha256=None, segment_size=None,
use_slo=True, metadata=None,
generate_checksums=True,
generate_checksums=None, data=None,
**headers):
"""Create a file object.
@ -7431,7 +7431,9 @@ class OpenStackCloud(_normalize.Normalizer):
This container will be created if it does not exist already.
:param name: Name for the object within the container.
:param filename: The path to the local file whose contents will be
uploaded.
uploaded. Mutually exclusive with data.
:param data: The content to upload to the object. Mutually exclusive
with filename.
:param md5: A hexadecimal md5 of the file. (Optional), if it is known
and can be passed here, it will save repeating the expensive md5
process. It is assumed to be accurate.
@ -7454,10 +7456,25 @@ class OpenStackCloud(_normalize.Normalizer):
:raises: ``OpenStackCloudException`` on operation error.
"""
if data is not None and filename:
raise ValueError(
"Both filename and data given. Please choose one.")
if data is not None and not name:
raise ValueError(
"name is a required parameter when data is given")
if data is not None and generate_checksums:
raise ValueError(
"checksums cannot be generated with data parameter")
if generate_checksums is None:
if data is not None:
generate_checksums = False
else:
generate_checksums = True
if not metadata:
metadata = {}
if not filename:
if not filename and data is None:
filename = name
# segment_size gets used as a step value in a range call, so needs
@ -7465,7 +7482,10 @@ class OpenStackCloud(_normalize.Normalizer):
if segment_size:
segment_size = int(segment_size)
segment_size = self.get_object_segment_size(segment_size)
file_size = os.path.getsize(filename)
if filename:
file_size = os.path.getsize(filename)
else:
file_size = len(data)
if generate_checksums and (md5 is None or sha256 is None):
(md5, sha256) = self._get_file_hashes(filename)
@ -7479,10 +7499,17 @@ class OpenStackCloud(_normalize.Normalizer):
# On some clouds this is not necessary. On others it is. I'm confused.
self.create_container(container)
endpoint = '{container}/{name}'.format(container=container, name=name)
if data is not None:
self.log.debug(
"swift uploading data to %(endpoint)s",
{'endpoint': endpoint})
return self._upload_object_data(endpoint, data, headers)
if self.is_object_stale(container, name, filename, md5, sha256):
endpoint = '{container}/{name}'.format(
container=container, name=name)
self.log.debug(
"swift uploading %(filename)s to %(endpoint)s",
{'filename': filename, 'endpoint': endpoint})
@ -7494,6 +7521,10 @@ class OpenStackCloud(_normalize.Normalizer):
endpoint, filename, headers,
file_size, segment_size, use_slo)
def _upload_object_data(self, endpoint, data, headers):
return self._object_store_client.put(
endpoint, headers=headers, data=data)
def _upload_object(self, endpoint, filename, headers):
return self._object_store_client.put(
endpoint, headers=headers, data=open(filename, 'r'))

View File

@ -594,7 +594,7 @@ class TestCase(base.TestCase):
key = '{method}|{uri}|{params}'.format(
method=method, uri=uri, params=kw_params)
validate = to_mock.pop('validate', {})
valid_keys = set(['json', 'headers', 'params'])
valid_keys = set(['json', 'headers', 'params', 'data'])
invalid_keys = set(validate.keys()) - valid_keys
if invalid_keys:
raise TypeError(

View File

@ -950,3 +950,55 @@ class TestObjectUploads(BaseTestObject):
generate_checksums=False)
self.assert_calls()
def test_create_object_data(self):
self.register_uris([
dict(method='GET',
uri='https://object-store.example.com/info',
json=dict(
swift={'max_file_size': 1000},
slo={'min_segment_size': 500})),
dict(method='HEAD',
uri='{endpoint}/{container}'.format(
endpoint=self.endpoint,
container=self.container),
status_code=404),
dict(method='PUT',
uri='{endpoint}/{container}'.format(
endpoint=self.endpoint, container=self.container),
status_code=201,
headers={
'Date': 'Fri, 16 Dec 2016 18:21:20 GMT',
'Content-Length': '0',
'Content-Type': 'text/html; charset=UTF-8',
}),
dict(method='HEAD',
uri='{endpoint}/{container}'.format(
endpoint=self.endpoint, container=self.container),
headers={
'Content-Length': '0',
'X-Container-Object-Count': '0',
'Accept-Ranges': 'bytes',
'X-Storage-Policy': 'Policy-0',
'Date': 'Fri, 16 Dec 2016 18:29:05 GMT',
'X-Timestamp': '1481912480.41664',
'X-Trans-Id': 'tx60ec128d9dbf44b9add68-0058543271dfw1',
'X-Container-Bytes-Used': '0',
'Content-Type': 'text/plain; charset=utf-8'}),
dict(method='PUT',
uri='{endpoint}/{container}/{object}'.format(
endpoint=self.endpoint,
container=self.container, object=self.object),
status_code=201,
validate=dict(
headers={},
data=self.content,
)),
])
self.cloud.create_object(
container=self.container, name=self.object,
data=self.content)
self.assert_calls()

View File

@ -0,0 +1,5 @@
---
features:
- |
Add a data parameter to ``openstack.connection.Connection.create_object``
so that data can be passed in directly instead of through a file.