Fix package function update with same md5
Do not update or delete the function package if updating the function with the same package but not providing the md5. Change-Id: Iea24978106984111b61b0b19336cf1eb9ded9ed6 Story: 2003482 Task: 24749
This commit is contained in:
parent
1448ac031a
commit
cd88b217ca
|
@ -224,7 +224,7 @@ class FunctionsController(rest.RestController):
|
||||||
if store:
|
if store:
|
||||||
try:
|
try:
|
||||||
ctx = context.get_ctx()
|
ctx = context.get_ctx()
|
||||||
actual_md5 = self.storage_provider.store(
|
_, actual_md5 = self.storage_provider.store(
|
||||||
ctx.projectid, func_db.id, data, md5sum=md5sum
|
ctx.projectid, func_db.id, data, md5sum=md5sum
|
||||||
)
|
)
|
||||||
values['code'].update({"md5sum": actual_md5})
|
values['code'].update({"md5sum": actual_md5})
|
||||||
|
@ -394,7 +394,7 @@ class FunctionsController(rest.RestController):
|
||||||
|
|
||||||
# Package type function. 'code' and 'entry' make sense only if
|
# Package type function. 'code' and 'entry' make sense only if
|
||||||
# 'package' is provided
|
# 'package' is provided
|
||||||
update_package = False
|
package_updated = False
|
||||||
if (pre_source == constants.PACKAGE_FUNCTION and
|
if (pre_source == constants.PACKAGE_FUNCTION and
|
||||||
values.get('package') is not None):
|
values.get('package') is not None):
|
||||||
if md5sum and md5sum == pre_md5sum:
|
if md5sum and md5sum == pre_md5sum:
|
||||||
|
@ -404,7 +404,7 @@ class FunctionsController(rest.RestController):
|
||||||
|
|
||||||
# Update the package data.
|
# Update the package data.
|
||||||
data = values['package'].file.read()
|
data = values['package'].file.read()
|
||||||
md5sum = self.storage_provider.store(
|
package_updated, md5sum = self.storage_provider.store(
|
||||||
ctx.projectid,
|
ctx.projectid,
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
|
@ -414,7 +414,6 @@ class FunctionsController(rest.RestController):
|
||||||
{"md5sum": md5sum, "source": pre_source}
|
{"md5sum": md5sum, "source": pre_source}
|
||||||
)
|
)
|
||||||
values.pop('package')
|
values.pop('package')
|
||||||
update_package = True
|
|
||||||
|
|
||||||
# Swift type function
|
# Swift type function
|
||||||
if pre_source == constants.SWIFT_FUNCTION:
|
if pre_source == constants.SWIFT_FUNCTION:
|
||||||
|
@ -429,7 +428,7 @@ class FunctionsController(rest.RestController):
|
||||||
func_db = db_api.update_function(id, values)
|
func_db = db_api.update_function(id, values)
|
||||||
|
|
||||||
# Delete the old function package if needed
|
# Delete the old function package if needed
|
||||||
if update_package:
|
if package_updated:
|
||||||
self.storage_provider.delete(ctx.projectid, id, pre_md5sum)
|
self.storage_provider.delete(ctx.projectid, id, pre_md5sum)
|
||||||
|
|
||||||
pecan.response.status = 200
|
pecan.response.status = 200
|
||||||
|
|
|
@ -35,7 +35,7 @@ class PackageStorage(object):
|
||||||
:param data: Package file content.
|
:param data: Package file content.
|
||||||
:param kwargs: A dict may including
|
:param kwargs: A dict may including
|
||||||
- md5sum: The MD5 provided by the user.
|
- md5sum: The MD5 provided by the user.
|
||||||
:return: MD5 value of the package.
|
:return: A tuple (if the package is updated, MD5 value of the package)
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class FileSystemStorage(base.PackageStorage):
|
||||||
:param function: Function ID.
|
:param function: Function ID.
|
||||||
:param data: Package file content.
|
:param data: Package file content.
|
||||||
:param md5sum: The MD5 provided by the user.
|
:param md5sum: The MD5 provided by the user.
|
||||||
:return: MD5 value of the package.
|
:return: A tuple (if the package is updated, MD5 value of the package)
|
||||||
"""
|
"""
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
'Store package, function: %s, project: %s', function, project_id
|
'Store package, function: %s, project: %s', function, project_id
|
||||||
|
@ -58,12 +58,15 @@ class FileSystemStorage(base.PackageStorage):
|
||||||
if md5sum and md5_actual != md5sum:
|
if md5sum and md5_actual != md5sum:
|
||||||
raise exc.InputException("Package md5 mismatch.")
|
raise exc.InputException("Package md5 mismatch.")
|
||||||
|
|
||||||
# The md5 is contained in the package path.
|
func_zip = os.path.join(
|
||||||
new_func_zip = os.path.join(project_path, '%s.zip.new' % function)
|
project_path,
|
||||||
func_zip = os.path.join(project_path,
|
PACKAGE_NAME_TEMPLATE % (function, md5_actual)
|
||||||
PACKAGE_NAME_TEMPLATE % (function, md5_actual))
|
)
|
||||||
|
if os.path.exists(func_zip):
|
||||||
|
return False, md5_actual
|
||||||
|
|
||||||
# Store package
|
# Save package
|
||||||
|
new_func_zip = os.path.join(project_path, '%s.zip.new' % function)
|
||||||
with open(new_func_zip, 'wb') as fd:
|
with open(new_func_zip, 'wb') as fd:
|
||||||
fd.write(data)
|
fd.write(data)
|
||||||
|
|
||||||
|
@ -72,7 +75,8 @@ class FileSystemStorage(base.PackageStorage):
|
||||||
raise exc.InputException("Package is not a valid ZIP package.")
|
raise exc.InputException("Package is not a valid ZIP package.")
|
||||||
|
|
||||||
os.rename(new_func_zip, func_zip)
|
os.rename(new_func_zip, func_zip)
|
||||||
return md5_actual
|
|
||||||
|
return True, md5_actual
|
||||||
|
|
||||||
def retrieve(self, project_id, function, md5sum, version=0):
|
def retrieve(self, project_id, function, md5sum, version=0):
|
||||||
"""Get function package data.
|
"""Get function package data.
|
||||||
|
|
|
@ -38,7 +38,7 @@ class TestFunctionController(base.APITest):
|
||||||
|
|
||||||
@mock.patch('qinling.storage.file_system.FileSystemStorage.store')
|
@mock.patch('qinling.storage.file_system.FileSystemStorage.store')
|
||||||
def test_post(self, mock_store):
|
def test_post(self, mock_store):
|
||||||
mock_store.return_value = 'fake_md5'
|
mock_store.return_value = (True, 'fake_md5')
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
body = {
|
body = {
|
||||||
|
@ -184,7 +184,7 @@ class TestFunctionController(base.APITest):
|
||||||
def test_put_package(self, mock_delete_func, mock_delete, mock_store,
|
def test_put_package(self, mock_delete_func, mock_delete, mock_store,
|
||||||
mock_etcd_del):
|
mock_etcd_del):
|
||||||
db_func = self.create_function(runtime_id=self.runtime_id)
|
db_func = self.create_function(runtime_id=self.runtime_id)
|
||||||
mock_store.return_value = "fake_md5_changed"
|
mock_store.return_value = (True, "fake_md5_changed")
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
resp = self.app.put(
|
resp = self.app.put(
|
||||||
|
@ -202,7 +202,30 @@ class TestFunctionController(base.APITest):
|
||||||
mock_delete.assert_called_once_with(unit_base.DEFAULT_PROJECT_ID,
|
mock_delete.assert_called_once_with(unit_base.DEFAULT_PROJECT_ID,
|
||||||
db_func.id, "fake_md5")
|
db_func.id, "fake_md5")
|
||||||
|
|
||||||
def test_put_package_same_md5(self):
|
@mock.patch('qinling.storage.file_system.FileSystemStorage.store')
|
||||||
|
@mock.patch('qinling.rpc.EngineClient.delete_function')
|
||||||
|
@mock.patch('qinling.utils.etcd_util.delete_function')
|
||||||
|
@mock.patch('qinling.storage.file_system.FileSystemStorage.delete')
|
||||||
|
def test_put_package_md5_not_change(self, file_delete_mock,
|
||||||
|
etcd_delete_mock, function_delete_mock,
|
||||||
|
store_mock):
|
||||||
|
db_func = self.create_function(runtime_id=self.runtime_id)
|
||||||
|
store_mock.return_value = (False, "fake_md5")
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
|
resp = self.app.put(
|
||||||
|
'/v1/functions/%s' % db_func.id,
|
||||||
|
params={},
|
||||||
|
upload_files=[('package', f.name, f.read())]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertEqual('fake_md5', resp.json['code'].get('md5sum'))
|
||||||
|
function_delete_mock.assert_called_once_with(db_func.id)
|
||||||
|
etcd_delete_mock.assert_called_once_with(db_func.id)
|
||||||
|
self.assertFalse(file_delete_mock.called)
|
||||||
|
|
||||||
|
def test_put_package_same_md5_provided(self):
|
||||||
db_func = self.create_function(runtime_id=self.runtime_id)
|
db_func = self.create_function(runtime_id=self.runtime_id)
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
|
|
|
@ -49,7 +49,12 @@ class TestFileSystemStorage(base.BaseTest):
|
||||||
function_data = "Some data".encode('utf8')
|
function_data = "Some data".encode('utf8')
|
||||||
md5 = common.md5(content=function_data)
|
md5 = common.md5(content=function_data)
|
||||||
|
|
||||||
self.storage.store(self.project_id, function, function_data)
|
package_updated, ret_md5 = self.storage.store(
|
||||||
|
self.project_id, function, function_data
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(package_updated)
|
||||||
|
self.assertEqual(md5, ret_md5)
|
||||||
|
|
||||||
temp_package_path = os.path.join(FAKE_STORAGE_PATH, self.project_id,
|
temp_package_path = os.path.join(FAKE_STORAGE_PATH, self.project_id,
|
||||||
'%s.zip.new' % function)
|
'%s.zip.new' % function)
|
||||||
|
@ -65,6 +70,29 @@ class TestFileSystemStorage(base.BaseTest):
|
||||||
is_zipfile_mock.assert_called_once_with(temp_package_path)
|
is_zipfile_mock.assert_called_once_with(temp_package_path)
|
||||||
rename_mock.assert_called_once_with(temp_package_path, package_path)
|
rename_mock.assert_called_once_with(temp_package_path, package_path)
|
||||||
|
|
||||||
|
@mock.patch('oslo_utils.fileutils.ensure_tree')
|
||||||
|
@mock.patch('os.path.exists')
|
||||||
|
def test_store_zip_exists(self, exists_mock, ensure_tree_mock):
|
||||||
|
function = self.rand_name('function', prefix='TestFileSystemStorage')
|
||||||
|
function_data = "Some data".encode('utf8')
|
||||||
|
md5 = common.md5(content=function_data)
|
||||||
|
exists_mock.return_value = True
|
||||||
|
|
||||||
|
package_updated, ret_md5 = self.storage.store(
|
||||||
|
self.project_id, function, function_data
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertFalse(package_updated)
|
||||||
|
self.assertEqual(md5, ret_md5)
|
||||||
|
|
||||||
|
package_path = os.path.join(
|
||||||
|
FAKE_STORAGE_PATH,
|
||||||
|
file_system.PACKAGE_PATH_TEMPLATE % (self.project_id, function,
|
||||||
|
md5)
|
||||||
|
)
|
||||||
|
|
||||||
|
exists_mock.assert_called_once_with(package_path)
|
||||||
|
|
||||||
@mock.patch('oslo_utils.fileutils.ensure_tree')
|
@mock.patch('oslo_utils.fileutils.ensure_tree')
|
||||||
def test_store_md5_mismatch(self, ensure_tree_mock):
|
def test_store_md5_mismatch(self, ensure_tree_mock):
|
||||||
function = self.rand_name('function', prefix='TestFileSystemStorage')
|
function = self.rand_name('function', prefix='TestFileSystemStorage')
|
||||||
|
|
Loading…
Reference in New Issue