Merge "Embed validation data in locations"
This commit is contained in:
commit
d2e62d4397
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import hashlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import glance_store
|
import glance_store
|
||||||
@ -45,6 +46,7 @@ CONF.import_opt('disk_formats', 'glance.common.config', group='image_format')
|
|||||||
CONF.import_opt('container_formats', 'glance.common.config',
|
CONF.import_opt('container_formats', 'glance.common.config',
|
||||||
group='image_format')
|
group='image_format')
|
||||||
CONF.import_opt('show_multiple_locations', 'glance.common.config')
|
CONF.import_opt('show_multiple_locations', 'glance.common.config')
|
||||||
|
CONF.import_opt('hashing_algorithm', 'glance.common.config')
|
||||||
|
|
||||||
|
|
||||||
class ImagesController(object):
|
class ImagesController(object):
|
||||||
@ -364,6 +366,76 @@ class ImagesController(object):
|
|||||||
except exception.NotAuthenticated as e:
|
except exception.NotAuthenticated as e:
|
||||||
raise webob.exc.HTTPUnauthorized(explanation=e.msg)
|
raise webob.exc.HTTPUnauthorized(explanation=e.msg)
|
||||||
|
|
||||||
|
def _validate_validation_data(self, image, locations):
|
||||||
|
val_data = {}
|
||||||
|
for loc in locations:
|
||||||
|
if 'validation_data' not in loc:
|
||||||
|
continue
|
||||||
|
for k, v in loc['validation_data'].items():
|
||||||
|
if val_data.get(k, v) != v:
|
||||||
|
msg = _("Conflicting values for %s") % k
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
val_data[k] = v
|
||||||
|
|
||||||
|
# NOTE(imacdonn): values may be provided for items which are
|
||||||
|
# already set, so long as the values exactly match. In this
|
||||||
|
# case, nothing actually needs to be updated, but we should
|
||||||
|
# reject the request if there's an apparent attempt to supply
|
||||||
|
# a different value.
|
||||||
|
new_val_data = {}
|
||||||
|
for k, v in val_data.items():
|
||||||
|
current = getattr(image, k)
|
||||||
|
if v == current:
|
||||||
|
continue
|
||||||
|
if current:
|
||||||
|
msg = _("%s is already set with a different value") % k
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
new_val_data[k] = v
|
||||||
|
|
||||||
|
if not new_val_data:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if image.status != 'queued':
|
||||||
|
msg = _("New value(s) for %s may only be provided when image "
|
||||||
|
"status is 'queued'") % ', '.join(new_val_data.keys())
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
if 'checksum' in new_val_data:
|
||||||
|
try:
|
||||||
|
checksum_bytes = bytearray.fromhex(new_val_data['checksum'])
|
||||||
|
except ValueError:
|
||||||
|
msg = (_("checksum (%s) is not a valid hexadecimal value") %
|
||||||
|
new_val_data['checksum'])
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
if len(checksum_bytes) != 16:
|
||||||
|
msg = (_("checksum (%s) is not the correct size for md5 "
|
||||||
|
"(should be 16 bytes)") %
|
||||||
|
new_val_data['checksum'])
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
hash_algo = new_val_data.get('os_hash_algo')
|
||||||
|
if hash_algo != CONF['hashing_algorithm']:
|
||||||
|
msg = (_("os_hash_algo must be %(want)s, not %(got)s") %
|
||||||
|
{'want': CONF['hashing_algorithm'], 'got': hash_algo})
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
hash_bytes = bytearray.fromhex(new_val_data['os_hash_value'])
|
||||||
|
except ValueError:
|
||||||
|
msg = (_("os_hash_value (%s) is not a valid hexadecimal value") %
|
||||||
|
new_val_data['os_hash_value'])
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
want_size = hashlib.new(hash_algo).digest_size
|
||||||
|
if len(hash_bytes) != want_size:
|
||||||
|
msg = (_("os_hash_value (%(value)s) is not the correct size for "
|
||||||
|
"%(algo)s (should be %(want)d bytes)") %
|
||||||
|
{'value': new_val_data['os_hash_value'],
|
||||||
|
'algo': hash_algo,
|
||||||
|
'want': want_size})
|
||||||
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
return new_val_data
|
||||||
|
|
||||||
def _get_locations_op_pos(self, path_pos, max_pos, allow_max):
|
def _get_locations_op_pos(self, path_pos, max_pos, allow_max):
|
||||||
if path_pos is None or max_pos is None:
|
if path_pos is None or max_pos is None:
|
||||||
return None
|
return None
|
||||||
@ -387,11 +459,15 @@ class ImagesController(object):
|
|||||||
"%s.") % image.status
|
"%s.") % image.status
|
||||||
raise webob.exc.HTTPConflict(explanation=msg)
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
val_data = self._validate_validation_data(image, value)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# NOTE(flwang): _locations_proxy's setattr method will check if
|
# NOTE(flwang): _locations_proxy's setattr method will check if
|
||||||
# the update is acceptable.
|
# the update is acceptable.
|
||||||
image.locations = value
|
image.locations = value
|
||||||
if image.status == 'queued':
|
if image.status == 'queued':
|
||||||
|
for k, v in val_data.items():
|
||||||
|
setattr(image, k, v)
|
||||||
image.status = 'active'
|
image.status = 'active'
|
||||||
except (exception.BadStoreUri, exception.DuplicateLocation) as e:
|
except (exception.BadStoreUri, exception.DuplicateLocation) as e:
|
||||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||||
@ -410,6 +486,8 @@ class ImagesController(object):
|
|||||||
"%s.") % image.status
|
"%s.") % image.status
|
||||||
raise webob.exc.HTTPConflict(explanation=msg)
|
raise webob.exc.HTTPConflict(explanation=msg)
|
||||||
|
|
||||||
|
val_data = self._validate_validation_data(image, [value])
|
||||||
|
|
||||||
pos = self._get_locations_op_pos(path_pos,
|
pos = self._get_locations_op_pos(path_pos,
|
||||||
len(image.locations), True)
|
len(image.locations), True)
|
||||||
if pos is None:
|
if pos is None:
|
||||||
@ -418,6 +496,8 @@ class ImagesController(object):
|
|||||||
try:
|
try:
|
||||||
image.locations.insert(pos, value)
|
image.locations.insert(pos, value)
|
||||||
if image.status == 'queued':
|
if image.status == 'queued':
|
||||||
|
for k, v in val_data.items():
|
||||||
|
setattr(image, k, v)
|
||||||
image.status = 'active'
|
image.status = 'active'
|
||||||
except (exception.BadStoreUri, exception.DuplicateLocation) as e:
|
except (exception.BadStoreUri, exception.DuplicateLocation) as e:
|
||||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||||
@ -1164,6 +1244,36 @@ def get_base_properties():
|
|||||||
'metadata': {
|
'metadata': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
},
|
},
|
||||||
|
'validation_data': {
|
||||||
|
'description': _(
|
||||||
|
'Values to be used to populate the corresponding '
|
||||||
|
'image properties. If the image status is not '
|
||||||
|
'\'queued\', values must exactly match those '
|
||||||
|
'already contained in the image properties.'
|
||||||
|
),
|
||||||
|
'type': 'object',
|
||||||
|
'writeOnly': True,
|
||||||
|
'additionalProperties': False,
|
||||||
|
'properties': {
|
||||||
|
'checksum': {
|
||||||
|
'type': 'string',
|
||||||
|
'minLength': 32,
|
||||||
|
'maxLength': 32,
|
||||||
|
},
|
||||||
|
'os_hash_algo': {
|
||||||
|
'type': 'string',
|
||||||
|
'maxLength': 64,
|
||||||
|
},
|
||||||
|
'os_hash_value': {
|
||||||
|
'type': 'string',
|
||||||
|
'maxLength': 128,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': [
|
||||||
|
'os_hash_algo',
|
||||||
|
'os_hash_value',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'required': ['url', 'metadata'],
|
'required': ['url', 'metadata'],
|
||||||
},
|
},
|
||||||
|
@ -1725,33 +1725,131 @@ class TestImagesController(base.IsolatedUnitTest):
|
|||||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||||
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||||
@mock.patch.object(store, 'get_size_from_backend')
|
@mock.patch.object(store, 'get_size_from_backend')
|
||||||
def test_replace_location_on_queued(self,
|
def test_replace_locations_on_queued(self,
|
||||||
mock_get_size,
|
mock_get_size,
|
||||||
mock_get_size_uri,
|
mock_get_size_uri,
|
||||||
mock_set_acls,
|
mock_set_acls,
|
||||||
mock_check_loc,
|
mock_check_loc,
|
||||||
mock_calc):
|
mock_calc):
|
||||||
mock_calc.return_value = 1
|
mock_calc.return_value = 1
|
||||||
mock_get_size.return_value = 1
|
mock_get_size.return_value = 1
|
||||||
mock_get_size_uri.return_value = 1
|
mock_get_size_uri.return_value = 1
|
||||||
self.config(show_multiple_locations=True)
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
self.images = [
|
self.images = [
|
||||||
_db_fixture('1', owner=TENANT1, checksum=CHKSUM,
|
_db_fixture(image_id, owner=TENANT1,
|
||||||
name='1',
|
name='1',
|
||||||
disk_format='raw',
|
disk_format='raw',
|
||||||
container_format='bare',
|
container_format='bare',
|
||||||
status='queued'),
|
status='queued',
|
||||||
|
checksum=None,
|
||||||
|
os_hash_algo=None,
|
||||||
|
os_hash_value=None),
|
||||||
]
|
]
|
||||||
self.db.image_create(None, self.images[0])
|
self.db.image_create(None, self.images[0])
|
||||||
request = unit_test_utils.get_fake_request()
|
request = unit_test_utils.get_fake_request()
|
||||||
new_location = {'url': '%s/fake_location_1' % BASE_URI, 'metadata': {}}
|
new_location1 = {'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {'checksum': CHKSUM,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1}}
|
||||||
|
new_location2 = {'url': '%s/fake_location_2' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {'checksum': CHKSUM,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1}}
|
||||||
|
changes = [{'op': 'replace', 'path': ['locations'],
|
||||||
|
'value': [new_location1, new_location2]}]
|
||||||
|
output = self.controller.update(request, image_id, changes)
|
||||||
|
self.assertEqual(image_id, output.image_id)
|
||||||
|
self.assertEqual(2, len(output.locations))
|
||||||
|
self.assertEqual(new_location1['url'], output.locations[0]['url'])
|
||||||
|
self.assertEqual(new_location2['url'], output.locations[1]['url'])
|
||||||
|
self.assertEqual('active', output.status)
|
||||||
|
self.assertEqual(CHKSUM, output.checksum)
|
||||||
|
self.assertEqual('sha512', output.os_hash_algo)
|
||||||
|
self.assertEqual(MULTIHASH1, output.os_hash_value)
|
||||||
|
|
||||||
|
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||||
|
@mock.patch.object(glance.location, '_check_image_location')
|
||||||
|
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||||
|
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||||
|
@mock.patch.object(store, 'get_size_from_backend')
|
||||||
|
def test_add_location_new_validation_data_on_active(self,
|
||||||
|
mock_get_size,
|
||||||
|
mock_get_size_uri,
|
||||||
|
mock_set_acls,
|
||||||
|
mock_check_loc,
|
||||||
|
mock_calc):
|
||||||
|
mock_calc.return_value = 1
|
||||||
|
mock_get_size.return_value = 1
|
||||||
|
mock_get_size_uri.return_value = 1
|
||||||
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
|
self.images = [
|
||||||
|
_db_fixture(image_id, owner=TENANT1,
|
||||||
|
name='1',
|
||||||
|
disk_format='raw',
|
||||||
|
container_format='bare',
|
||||||
|
status='active',
|
||||||
|
checksum=None,
|
||||||
|
os_hash_algo=None,
|
||||||
|
os_hash_value=None),
|
||||||
|
]
|
||||||
|
self.db.image_create(None, self.images[0])
|
||||||
|
request = unit_test_utils.get_fake_request()
|
||||||
|
new_location = {'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {'checksum': CHKSUM,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1}}
|
||||||
|
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||||
|
'value': new_location}]
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
"may only be provided when image status "
|
||||||
|
"is 'queued'",
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
|
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||||
|
@mock.patch.object(glance.location, '_check_image_location')
|
||||||
|
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||||
|
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||||
|
@mock.patch.object(store, 'get_size_from_backend')
|
||||||
|
def test_replace_locations_different_validation_data(self,
|
||||||
|
mock_get_size,
|
||||||
|
mock_get_size_uri,
|
||||||
|
mock_set_acls,
|
||||||
|
mock_check_loc,
|
||||||
|
mock_calc):
|
||||||
|
mock_calc.return_value = 1
|
||||||
|
mock_get_size.return_value = 1
|
||||||
|
mock_get_size_uri.return_value = 1
|
||||||
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
|
self.images = [
|
||||||
|
_db_fixture(image_id, owner=TENANT1,
|
||||||
|
name='1',
|
||||||
|
disk_format='raw',
|
||||||
|
container_format='bare',
|
||||||
|
status='active',
|
||||||
|
checksum=CHKSUM,
|
||||||
|
os_hash_algo='sha512',
|
||||||
|
os_hash_value=MULTIHASH1),
|
||||||
|
]
|
||||||
|
self.db.image_create(None, self.images[0])
|
||||||
|
request = unit_test_utils.get_fake_request()
|
||||||
|
new_location = {'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {'checksum': CHKSUM1,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH2}}
|
||||||
changes = [{'op': 'replace', 'path': ['locations'],
|
changes = [{'op': 'replace', 'path': ['locations'],
|
||||||
'value': [new_location]}]
|
'value': [new_location]}]
|
||||||
output = self.controller.update(request, '1', changes)
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
self.assertEqual('1', output.image_id)
|
"already set with a different value",
|
||||||
self.assertEqual(1, len(output.locations))
|
self.controller.update,
|
||||||
self.assertEqual(new_location, output.locations[0])
|
request, image_id, changes)
|
||||||
self.assertEqual('active', output.status)
|
|
||||||
|
|
||||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||||
@mock.patch.object(glance.location, '_check_image_location')
|
@mock.patch.object(glance.location, '_check_image_location')
|
||||||
@ -1768,8 +1866,9 @@ class TestImagesController(base.IsolatedUnitTest):
|
|||||||
mock_get_size.return_value = 1
|
mock_get_size.return_value = 1
|
||||||
mock_get_size_uri.return_value = 1
|
mock_get_size_uri.return_value = 1
|
||||||
self.config(show_multiple_locations=True)
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
self.images = [
|
self.images = [
|
||||||
_db_fixture('1', owner=TENANT1, checksum=CHKSUM,
|
_db_fixture(image_id, owner=TENANT1, checksum=CHKSUM,
|
||||||
name='1',
|
name='1',
|
||||||
disk_format='raw',
|
disk_format='raw',
|
||||||
container_format='bare',
|
container_format='bare',
|
||||||
@ -1777,15 +1876,189 @@ class TestImagesController(base.IsolatedUnitTest):
|
|||||||
]
|
]
|
||||||
self.db.image_create(None, self.images[0])
|
self.db.image_create(None, self.images[0])
|
||||||
request = unit_test_utils.get_fake_request()
|
request = unit_test_utils.get_fake_request()
|
||||||
new_location = {'url': '%s/fake_location_1' % BASE_URI, 'metadata': {}}
|
new_location = {'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {}}
|
||||||
changes = [{'op': 'add', 'path': ['locations', '-'],
|
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||||
'value': new_location}]
|
'value': new_location}]
|
||||||
output = self.controller.update(request, '1', changes)
|
output = self.controller.update(request, image_id, changes)
|
||||||
self.assertEqual('1', output.image_id)
|
self.assertEqual(image_id, output.image_id)
|
||||||
self.assertEqual(1, len(output.locations))
|
self.assertEqual(1, len(output.locations))
|
||||||
self.assertEqual(new_location, output.locations[0])
|
self.assertEqual(new_location, output.locations[0])
|
||||||
self.assertEqual('active', output.status)
|
self.assertEqual('active', output.status)
|
||||||
|
|
||||||
|
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||||
|
@mock.patch.object(glance.location, '_check_image_location')
|
||||||
|
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||||
|
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||||
|
@mock.patch.object(store, 'get_size_from_backend')
|
||||||
|
def test_add_location_invalid_validation_data(self,
|
||||||
|
mock_get_size,
|
||||||
|
mock_get_size_uri,
|
||||||
|
mock_set_acls,
|
||||||
|
mock_check_loc,
|
||||||
|
mock_calc):
|
||||||
|
mock_calc.return_value = 1
|
||||||
|
mock_get_size.return_value = 1
|
||||||
|
mock_get_size_uri.return_value = 1
|
||||||
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
|
self.images = [
|
||||||
|
_db_fixture(image_id, owner=TENANT1,
|
||||||
|
checksum=None,
|
||||||
|
os_hash_algo=None,
|
||||||
|
os_hash_value=None,
|
||||||
|
name='1',
|
||||||
|
disk_format='raw',
|
||||||
|
container_format='bare',
|
||||||
|
status='queued'),
|
||||||
|
]
|
||||||
|
self.db.image_create(None, self.images[0])
|
||||||
|
request = unit_test_utils.get_fake_request()
|
||||||
|
|
||||||
|
location = {
|
||||||
|
'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {}
|
||||||
|
}
|
||||||
|
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||||
|
'value': location}]
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'checksum': 'something the same length as md5',
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1,
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
'checksum .* is not a valid hexadecimal value',
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'checksum': '0123456789abcdef',
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1,
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
'checksum .* is not the correct size',
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'checksum': CHKSUM,
|
||||||
|
'os_hash_algo': 'sha256',
|
||||||
|
'os_hash_value': MULTIHASH1,
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
'os_hash_algo must be sha512',
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'checksum': CHKSUM,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': 'not a hex value',
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
'os_hash_value .* is not a valid hexadecimal '
|
||||||
|
'value',
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'checksum': CHKSUM,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': '0123456789abcdef',
|
||||||
|
}
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
'os_hash_value .* is not the correct size '
|
||||||
|
'for sha512',
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
|
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||||
|
@mock.patch.object(glance.location, '_check_image_location')
|
||||||
|
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||||
|
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||||
|
@mock.patch.object(store, 'get_size_from_backend')
|
||||||
|
def test_add_location_same_validation_data(self,
|
||||||
|
mock_get_size,
|
||||||
|
mock_get_size_uri,
|
||||||
|
mock_set_acls,
|
||||||
|
mock_check_loc,
|
||||||
|
mock_calc):
|
||||||
|
mock_calc.return_value = 1
|
||||||
|
mock_get_size.return_value = 1
|
||||||
|
mock_get_size_uri.return_value = 1
|
||||||
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
|
os_hash_value = '6513f21e44aa3da349f248188a44bc304a3653a04122d8fb45' \
|
||||||
|
'35423c8e1d14cd6a153f735bb0982e2161b5b5186106570c17' \
|
||||||
|
'a9e58b64dd39390617cd5a350f78'
|
||||||
|
self.images = [
|
||||||
|
_db_fixture(image_id, owner=TENANT1,
|
||||||
|
name='1',
|
||||||
|
disk_format='raw',
|
||||||
|
container_format='bare',
|
||||||
|
status='active',
|
||||||
|
checksum='checksum1',
|
||||||
|
os_hash_algo='sha512',
|
||||||
|
os_hash_value=os_hash_value),
|
||||||
|
]
|
||||||
|
self.db.image_create(None, self.images[0])
|
||||||
|
request = unit_test_utils.get_fake_request()
|
||||||
|
new_location = {'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {'checksum': 'checksum1',
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': os_hash_value}}
|
||||||
|
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||||
|
'value': new_location}]
|
||||||
|
output = self.controller.update(request, image_id, changes)
|
||||||
|
self.assertEqual(image_id, output.image_id)
|
||||||
|
self.assertEqual(1, len(output.locations))
|
||||||
|
self.assertEqual(new_location, output.locations[0])
|
||||||
|
self.assertEqual('active', output.status)
|
||||||
|
|
||||||
|
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||||
|
@mock.patch.object(glance.location, '_check_image_location')
|
||||||
|
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||||
|
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||||
|
@mock.patch.object(store, 'get_size_from_backend')
|
||||||
|
def test_add_location_different_validation_data(self,
|
||||||
|
mock_get_size,
|
||||||
|
mock_get_size_uri,
|
||||||
|
mock_set_acls,
|
||||||
|
mock_check_loc,
|
||||||
|
mock_calc):
|
||||||
|
mock_calc.return_value = 1
|
||||||
|
mock_get_size.return_value = 1
|
||||||
|
mock_get_size_uri.return_value = 1
|
||||||
|
self.config(show_multiple_locations=True)
|
||||||
|
image_id = str(uuid.uuid4())
|
||||||
|
self.images = [
|
||||||
|
_db_fixture(image_id, owner=TENANT1,
|
||||||
|
name='1',
|
||||||
|
disk_format='raw',
|
||||||
|
container_format='bare',
|
||||||
|
status='active',
|
||||||
|
checksum=CHKSUM,
|
||||||
|
os_hash_algo='sha512',
|
||||||
|
os_hash_value=MULTIHASH1),
|
||||||
|
]
|
||||||
|
self.db.image_create(None, self.images[0])
|
||||||
|
request = unit_test_utils.get_fake_request()
|
||||||
|
new_location = {'url': '%s/fake_location_1' % BASE_URI,
|
||||||
|
'metadata': {},
|
||||||
|
'validation_data': {'checksum': CHKSUM1,
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH2}}
|
||||||
|
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||||
|
'value': new_location}]
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPConflict,
|
||||||
|
"already set with a different value",
|
||||||
|
self.controller.update,
|
||||||
|
request, image_id, changes)
|
||||||
|
|
||||||
def _test_update_locations_status(self, image_status, update):
|
def _test_update_locations_status(self, image_status, update):
|
||||||
self.config(show_multiple_locations=True)
|
self.config(show_multiple_locations=True)
|
||||||
self.images = [
|
self.images = [
|
||||||
@ -2696,6 +2969,44 @@ class TestImagesDeserializer(test_utils.BaseTestCase):
|
|||||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.deserializer.update, request)
|
self.deserializer.update, request)
|
||||||
|
|
||||||
|
def test_update_invalid_validation_data(self):
|
||||||
|
request = self._get_fake_patch_request()
|
||||||
|
changes = [{
|
||||||
|
'op': 'add',
|
||||||
|
'path': '/locations/0',
|
||||||
|
'value': {
|
||||||
|
'url': 'http://localhost/fake',
|
||||||
|
'metadata': {},
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1,
|
||||||
|
'checksum': CHKSUM,
|
||||||
|
}
|
||||||
|
request.body = jsonutils.dump_as_bytes(changes)
|
||||||
|
self.deserializer.update(request)
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': MULTIHASH1,
|
||||||
|
'checksum': CHKSUM,
|
||||||
|
'bogus_key': 'bogus_value',
|
||||||
|
}
|
||||||
|
request.body = jsonutils.dump_as_bytes(changes)
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPBadRequest,
|
||||||
|
'Additional properties are not allowed',
|
||||||
|
self.deserializer.update, request)
|
||||||
|
|
||||||
|
changes[0]['value']['validation_data'] = {
|
||||||
|
'checksum': CHKSUM,
|
||||||
|
}
|
||||||
|
request.body = jsonutils.dump_as_bytes(changes)
|
||||||
|
self.assertRaisesRegexp(webob.exc.HTTPBadRequest,
|
||||||
|
'os_hash.* is a required property',
|
||||||
|
self.deserializer.update, request)
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
request = self._get_fake_patch_request()
|
request = self._get_fake_patch_request()
|
||||||
body = [
|
body = [
|
||||||
|
Loading…
Reference in New Issue
Block a user