Merge "Handle create/update of images with unknown size"
This commit is contained in:
@@ -143,6 +143,18 @@ class HTTPClient(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
conn_url = os.path.normpath('%s/%s' % (self.endpoint_path, url))
|
conn_url = os.path.normpath('%s/%s' % (self.endpoint_path, url))
|
||||||
|
if kwargs['headers'].get('Transfer-Encoding') == 'chunked':
|
||||||
|
conn.putrequest(method, conn_url)
|
||||||
|
for header, value in kwargs['headers'].items():
|
||||||
|
conn.putheader(header, value)
|
||||||
|
conn.endheaders()
|
||||||
|
chunk = kwargs['body'].read(CHUNKSIZE)
|
||||||
|
# Chunk it, baby...
|
||||||
|
while chunk:
|
||||||
|
conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
|
||||||
|
chunk = kwargs['body'].read(CHUNKSIZE)
|
||||||
|
conn.send('0\r\n\r\n')
|
||||||
|
else:
|
||||||
conn.request(method, conn_url, **kwargs)
|
conn.request(method, conn_url, **kwargs)
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
except socket.gaierror as e:
|
except socket.gaierror as e:
|
||||||
@@ -198,6 +210,12 @@ class HTTPClient(object):
|
|||||||
kwargs.setdefault('headers', {})
|
kwargs.setdefault('headers', {})
|
||||||
kwargs['headers'].setdefault('Content-Type',
|
kwargs['headers'].setdefault('Content-Type',
|
||||||
'application/octet-stream')
|
'application/octet-stream')
|
||||||
|
if 'body' in kwargs:
|
||||||
|
if (hasattr(kwargs['body'], 'read')
|
||||||
|
and method.lower() in ('post', 'put')):
|
||||||
|
# We use 'Transfer-Encoding: chunked' because
|
||||||
|
# body size may not always be known in advance.
|
||||||
|
kwargs['headers']['Transfer-Encoding'] = 'chunked'
|
||||||
return self._http_request(url, method, **kwargs)
|
return self._http_request(url, method, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -177,10 +177,15 @@ class ImageManager(base.Manager):
|
|||||||
# Illegal seek. This means the user is trying
|
# Illegal seek. This means the user is trying
|
||||||
# to pipe image data to the client, e.g.
|
# to pipe image data to the client, e.g.
|
||||||
# echo testdata | bin/glance add blah..., or
|
# echo testdata | bin/glance add blah..., or
|
||||||
# that stdin is empty
|
# that stdin is empty, or that a file-like
|
||||||
return 0
|
# object which doesn't support 'seek/tell' has
|
||||||
|
# been supplied.
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
else:
|
||||||
|
# Cannot determine size of input image
|
||||||
|
return None
|
||||||
|
|
||||||
def create(self, **kwargs):
|
def create(self, **kwargs):
|
||||||
"""Create an image
|
"""Create an image
|
||||||
@@ -190,10 +195,8 @@ class ImageManager(base.Manager):
|
|||||||
image_data = kwargs.pop('data', None)
|
image_data = kwargs.pop('data', None)
|
||||||
if image_data is not None:
|
if image_data is not None:
|
||||||
image_size = self._get_file_size(image_data)
|
image_size = self._get_file_size(image_data)
|
||||||
if image_size != 0:
|
if image_size is not None:
|
||||||
kwargs.setdefault('size', image_size)
|
kwargs.setdefault('size', image_size)
|
||||||
else:
|
|
||||||
image_data = None
|
|
||||||
|
|
||||||
fields = {}
|
fields = {}
|
||||||
for field in kwargs:
|
for field in kwargs:
|
||||||
@@ -218,16 +221,13 @@ class ImageManager(base.Manager):
|
|||||||
|
|
||||||
TODO(bcwaldon): document accepted params
|
TODO(bcwaldon): document accepted params
|
||||||
"""
|
"""
|
||||||
hdrs = {}
|
|
||||||
image_data = kwargs.pop('data', None)
|
image_data = kwargs.pop('data', None)
|
||||||
if image_data is not None:
|
if image_data is not None:
|
||||||
image_size = self._get_file_size(image_data)
|
image_size = self._get_file_size(image_data)
|
||||||
if image_size != 0:
|
if image_size is not None:
|
||||||
kwargs.setdefault('size', image_size)
|
kwargs.setdefault('size', image_size)
|
||||||
hdrs['Content-Length'] = image_size
|
|
||||||
else:
|
|
||||||
image_data = None
|
|
||||||
|
|
||||||
|
hdrs = {}
|
||||||
try:
|
try:
|
||||||
purge_props = 'true' if kwargs.pop('purge_props') else 'false'
|
purge_props = 'true' if kwargs.pop('purge_props') else 'false'
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@@ -81,9 +81,20 @@ def _set_data_field(fields, args):
|
|||||||
if args.file:
|
if args.file:
|
||||||
fields['data'] = open(args.file, 'rb')
|
fields['data'] = open(args.file, 'rb')
|
||||||
else:
|
else:
|
||||||
|
# We distinguish between cases where image data is pipelined:
|
||||||
|
# (1) glance ... < /tmp/file or cat /tmp/file | glance ...
|
||||||
|
# and cases where no image data is provided:
|
||||||
|
# (2) glance ...
|
||||||
|
if (sys.stdin.isatty() is not True):
|
||||||
|
# Our input is from stdin, and we are part of
|
||||||
|
# a pipeline, so data may be present. (We are of
|
||||||
|
# type (1) above.)
|
||||||
if msvcrt:
|
if msvcrt:
|
||||||
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
||||||
fields['data'] = sys.stdin
|
fields['data'] = sys.stdin
|
||||||
|
else:
|
||||||
|
# We are of type (2) above, no image data supplied
|
||||||
|
fields['data'] = None
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to describe.')
|
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to describe.')
|
||||||
|
@@ -390,7 +390,7 @@ class ImageManagerTest(unittest.TestCase):
|
|||||||
def test_update_with_data(self):
|
def test_update_with_data(self):
|
||||||
image_data = StringIO.StringIO('XXX')
|
image_data = StringIO.StringIO('XXX')
|
||||||
self.mgr.update('1', data=image_data)
|
self.mgr.update('1', data=image_data)
|
||||||
expect_headers = {'x-image-meta-size': '3', 'Content-Length': 3}
|
expect_headers = {'x-image-meta-size': '3'}
|
||||||
expect = [('PUT', '/v1/images/1', expect_headers, image_data)]
|
expect = [('PUT', '/v1/images/1', expect_headers, image_data)]
|
||||||
self.assertEqual(self.api.calls, expect)
|
self.assertEqual(self.api.calls, expect)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user