Only delete 'owned' packages for --exists-action update

Due to the fact that fqns are no longer unique update might currently
not work correctly, because it searches for package to delete by fqn.
To improve this situation this patch attempts to search for the package
in the current tenant and deletes it by id.
In case it is not found - raise the error, cause it most likely means
that the package is in another tennant and deleting it automatically is
dangerous.
This change relies on 'owned' parameter in api sorting, fixed
in https://review.openstack.org/#/c/177796/

Change-Id: Iee04ea65f5c4937ded4e259e3c0cb64189801da2
Closes-Bug: #1448135
This commit is contained in:
Kirill Zaitsev 2015-04-29 14:04:02 +03:00
parent 33d116914c
commit 22a28daa14
2 changed files with 154 additions and 3 deletions

View File

@ -27,6 +27,7 @@ import requests_mock
import six
from testtools import matchers
from muranoclient.common import exceptions as common_exceptions
from muranoclient.common import utils
from muranoclient.openstack.common.apiclient import exceptions
import muranoclient.shell
@ -335,6 +336,136 @@ class ShellPackagesOperations(ShellTest):
'is_public': True
}, {RESULT_PACKAGE: mock.ANY},)
def _test_conflict(self,
packages, from_file, raw_input_mock,
input_action, exists_action=''):
packages.create = mock.MagicMock(
side_effect=[common_exceptions.HTTPConflict("Conflict"), None])
packages.filter.return_value = [mock.Mock(id='test_id')]
raw_input_mock.return_value = input_action
args = TestArgs()
args.exists_action = exists_action
with tempfile.NamedTemporaryFile() as f:
args.filename = f.name
pkg = make_pkg({'FullName': f.name})
from_file.return_value = utils.Package(utils.File(pkg))
v1_shell.do_package_import(self.client, args)
return f.name
@mock.patch('__builtin__.raw_input')
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_conflict_skip(self, from_file, raw_input_mock):
name = self._test_conflict(
self.client.packages,
from_file,
raw_input_mock,
's',
)
self.client.packages.create.assert_called_once_with({
'is_public': False,
}, {name: mock.ANY},)
@mock.patch('__builtin__.raw_input')
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_conflict_skip_ea(self, from_file, raw_input_mock):
name = self._test_conflict(
self.client.packages,
from_file,
raw_input_mock,
'',
exists_action='s',
)
self.client.packages.create.assert_called_once_with({
'is_public': False,
}, {name: mock.ANY},)
self.assertFalse(raw_input_mock.called)
@mock.patch('__builtin__.raw_input')
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_conflict_abort(self, from_file, raw_input_mock):
self.assertRaises(SystemExit, self._test_conflict,
self.client.packages,
from_file,
raw_input_mock,
'a',
)
self.client.packages.create.assert_called_once_with({
'is_public': False,
}, mock.ANY,)
@mock.patch('__builtin__.raw_input')
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_conflict_abort_ea(self,
from_file, raw_input_mock):
self.assertRaises(SystemExit, self._test_conflict,
self.client.packages,
from_file,
raw_input_mock,
'',
exists_action='a',
)
self.client.packages.create.assert_called_once_with({
'is_public': False,
}, mock.ANY,)
self.assertFalse(raw_input_mock.called)
@mock.patch('__builtin__.raw_input')
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_conflict_update(self, from_file, raw_input_mock):
name = self._test_conflict(
self.client.packages,
from_file,
raw_input_mock,
'u',
)
self.client.packages.delete.assert_called_once_with('test_id')
self.client.packages.create.assert_has_calls(
[
mock.call({'is_public': False}, {name: mock.ANY},),
mock.call({'is_public': False}, {name: mock.ANY},)
], any_order=True,
)
self.assertEqual(self.client.packages.create.call_count, 2)
@mock.patch('__builtin__.raw_input')
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_conflict_update_ea(self,
from_file, raw_input_mock):
name = self._test_conflict(
self.client.packages,
from_file,
raw_input_mock,
'',
exists_action='u',
)
self.client.packages.delete.assert_called_once_with('test_id')
self.client.packages.create.assert_has_calls(
[
mock.call({'is_public': False}, {name: mock.ANY},),
mock.call({'is_public': False}, {name: mock.ANY},)
], any_order=True,
)
self.assertEqual(self.client.packages.create.call_count, 2)
self.assertFalse(raw_input_mock.called)
@mock.patch('muranoclient.common.utils.Package.from_file')
def test_package_import_no_categories(self, from_file):
args = TestArgs()

View File

@ -280,7 +280,8 @@ def _handle_package_exists(mc, data, package, exists_action):
try:
return mc.packages.create(data, {name: package.file()})
except common_exceptions.HTTPConflict:
print("Package with name {0} is already registered.".format(name))
print("Importing package {0} failed. Package with the same"
" name/classes is already registered.".format(name))
allowed_results = ['s', 'u', 'a']
res = exists_action
if not res:
@ -296,8 +297,23 @@ def _handle_package_exists(mc, data, package, exists_action):
print("Exiting.")
sys.exit()
elif res == 'u':
print("Deleting package {0}".format(name))
mc.packages.delete(name)
pkgs = list(mc.packages.filter(fqn=name, owned=True))
if not pkgs:
msg = (
"Got Conflict response, but couldn't find package "
"'{0}' in the current tenant.\nThis probably means "
"conflicting package is in another tenant.\n"
"Please delete it manually."
).format(name)
raise exceptions.CommandError(msg)
elif len(pkgs) > 1:
msg = (
"Got {0} packages with name '{1}'.\nI'm not trusting "
"myself, please delete the package manually"
).format(len(pkgs), name)
raise exceptions.CommandError(msg)
print("Deleting package {0}({1})".format(name, pkgs[0].id))
mc.packages.delete(pkgs[0].id)
continue
@ -358,6 +374,8 @@ def do_package_import(mc, args):
"images for {1}".format(e, name))
try:
_handle_package_exists(mc, data, package, args.exists_action)
except exceptions.CommandError:
raise
except Exception as e:
print("Error {0} occurred while installing package {1}".format(
e, name))
@ -426,6 +444,8 @@ def do_bundle_import(mc, args):
try:
_handle_package_exists(
mc, data, dep_package, args.exists_action)
except exceptions.CommandError:
raise
except Exception as e:
print("Error {0} occurred while "
"installing package {1}".format(e, name))