Add --remove option for fuel plugins
This option deletes the plugin that was previously installed. Optionally ==<version> can be specified as in: fuel plugins --remove fuel_plugin_example==1.0.0 DocImpact related to blueprint cinder-neutron-plugins-in-fuel Change-Id: I8d8ac913c74710843c4a617fac214625af7eda1e
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from fuelclient.cli import error
|
||||
|
||||
from fuelclient.cli.actions.base import Action
|
||||
import fuelclient.cli.arguments as Args
|
||||
from fuelclient.cli.formatting import format_table
|
||||
@@ -35,10 +37,12 @@ class PluginAction(Action):
|
||||
self.args = [
|
||||
Args.get_list_arg("List all available plugins."),
|
||||
Args.get_plugin_install_arg("Install action"),
|
||||
Args.get_plugin_remove_arg("Remove action"),
|
||||
Args.get_force_arg("Update action"),
|
||||
]
|
||||
self.flag_func_map = (
|
||||
("install", self.install),
|
||||
("remove", self.remove),
|
||||
(None, self.list),
|
||||
)
|
||||
|
||||
@@ -65,3 +69,22 @@ class PluginAction(Action):
|
||||
results,
|
||||
"Plugin {0} was successfully installed.".format(
|
||||
params.install))
|
||||
|
||||
def remove(self, params):
|
||||
"""Remove plugin from environment
|
||||
fuel plugins --remove plugin_sample
|
||||
fuel plugins --remove plugin_sample==1.0.1
|
||||
"""
|
||||
s = params.remove.split('==')
|
||||
plugin_name = s[0]
|
||||
plugin_version = None
|
||||
if len(s) == 2:
|
||||
plugin_version = s[1]
|
||||
elif len(s) > 2:
|
||||
raise error.ArgumentException(
|
||||
'Syntax: fuel plugins --remove fuel_plugin==1.0')
|
||||
results = Plugins.remove_plugin(
|
||||
plugin_name, plugin_version=plugin_version)
|
||||
self.serializer.print_to_output(
|
||||
results,
|
||||
"Plugin {0} was successfully removed.".format(params.remove))
|
||||
|
||||
@@ -395,3 +395,11 @@ def get_plugin_install_arg(help_msg):
|
||||
flags=("--install",),
|
||||
help=help_msg
|
||||
)
|
||||
|
||||
|
||||
def get_plugin_remove_arg(help_msg):
|
||||
return get_str_arg(
|
||||
"remove",
|
||||
flags=("--remove",),
|
||||
help=help_msg
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
|
||||
import yaml
|
||||
@@ -41,8 +42,45 @@ class Plugins(base.BaseObject):
|
||||
for member_name in plugin_tar.getnames():
|
||||
if cls.metadata_config in member_name:
|
||||
return yaml.load(plugin_tar.extractfile(member_name).read())
|
||||
raise error.BadDataException("Tarfile {0} doesn't have {1}".format(
|
||||
plugin_tar.name, cls.metadata_config))
|
||||
raise error.BadDataException(
|
||||
"Tarfile {name} doesn't have {config}".format(
|
||||
name=plugin_tar.name, config=cls.metadata_config))
|
||||
|
||||
@classmethod
|
||||
def get_plugin(cls, plugin_name, plugin_version=None):
|
||||
"""Returns plugin fetched by name and optionally by version.
|
||||
|
||||
If multiple plugin versions are found and version is not specified
|
||||
error is returned.
|
||||
|
||||
:returns: dictionary with plugin data
|
||||
"""
|
||||
checker = lambda p: p['name'] == plugin_name
|
||||
if plugin_version is not None:
|
||||
checker = lambda p: \
|
||||
(p['name'], p['version']) == (plugin_name, plugin_version)
|
||||
plugins = filter(checker, cls.get_all_data())
|
||||
if len(plugins) == 0:
|
||||
if plugin_version is None:
|
||||
raise error.BadDataException(
|
||||
'Plugin {plugin_name} does not exist'.format(
|
||||
plugin_name=plugin_name)
|
||||
)
|
||||
else:
|
||||
raise error.BadDataException(
|
||||
'Plugin {plugin_name}, version {plugin_version} does '
|
||||
'not exist'.format(
|
||||
plugin_name=plugin_name,
|
||||
plugin_version=plugin_version)
|
||||
)
|
||||
if len(plugins) > 1 and plugin_version is None:
|
||||
raise error.BadDataException(
|
||||
'Multiple versions of plugin {plugin_name} found. '
|
||||
'Please consider specifying a version in the '
|
||||
'{plugin_name}==<version> format'.format(
|
||||
plugin_name=plugin_name)
|
||||
)
|
||||
return plugins[0]
|
||||
|
||||
@classmethod
|
||||
def add_plugin(cls, plugin_meta, plugin_tar):
|
||||
@@ -68,3 +106,22 @@ class Plugins(base.BaseObject):
|
||||
finally:
|
||||
plugin_tar.close()
|
||||
return resp
|
||||
|
||||
@classmethod
|
||||
def remove_plugin(cls, plugin_name, plugin_version=None):
|
||||
if not cls.validate_environment():
|
||||
raise error.WrongEnvironmentError(
|
||||
'Plugin can be removed only from master node.')
|
||||
plugin = cls.get_plugin(plugin_name, plugin_version)
|
||||
|
||||
resp = cls.connection.delete_request(
|
||||
cls.class_instance_path.format(**plugin)
|
||||
)
|
||||
|
||||
plugin_path = os.path.join(
|
||||
EXTRACT_PATH,
|
||||
'{name}-{version}'.format(**plugin)
|
||||
)
|
||||
shutil.rmtree(plugin_path)
|
||||
|
||||
return resp
|
||||
|
||||
@@ -60,3 +60,123 @@ class TestPluginsActions(base.UnitTestCase):
|
||||
['fuel', 'plugins', '--install', '/tmp/sample.fp', '--force'])
|
||||
self.assertEqual(mrequests.post.call_count, 1)
|
||||
self.assertEqual(mrequests.put.call_count, 1)
|
||||
|
||||
@patch('fuelclient.objects.plugins.os')
|
||||
@patch('fuelclient.objects.plugins.shutil')
|
||||
def test_remove_plugin_single(self, mshutil, mos, mrequests):
|
||||
mos.path.exists.return_value = True
|
||||
mresponse = Mock(status_code=201)
|
||||
mresponse.json.return_value = [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'test',
|
||||
'version': '1.0.0',
|
||||
}
|
||||
]
|
||||
mrequests.get.return_value = mresponse
|
||||
mrequests.delete.return_value = Mock(status_code=200)
|
||||
|
||||
self.execute(
|
||||
['fuel', 'plugins', '--remove', 'test']
|
||||
)
|
||||
|
||||
self.assertEqual(mrequests.delete.call_count, 1)
|
||||
self.assertEqual(mshutil.rmtree.call_count, 1)
|
||||
self.assertEqual(
|
||||
'test-1.0.0',
|
||||
mos.path.join.call_args[0][1])
|
||||
|
||||
@patch('fuelclient.objects.plugins.os')
|
||||
@patch('fuelclient.objects.plugins.shutil')
|
||||
def test_remove_plugin_multi(self, mshutil, mos, mrequests):
|
||||
mos.path.exists.return_value = True
|
||||
mresponse = Mock(status_code=201)
|
||||
mresponse.json.return_value = [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'test',
|
||||
'version': '1.0.0',
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': 'test',
|
||||
'version': '1.1.0',
|
||||
}
|
||||
]
|
||||
mrequests.get.return_value = mresponse
|
||||
mrequests.delete.return_value = Mock(status_code=200)
|
||||
|
||||
self.execute(
|
||||
['fuel', 'plugins', '--remove', 'test==1.0.0']
|
||||
)
|
||||
|
||||
self.assertEqual(mrequests.delete.call_count, 1)
|
||||
self.assertEqual(mshutil.rmtree.call_count, 1)
|
||||
|
||||
@patch('fuelclient.objects.plugins.os')
|
||||
def test_remove_nonexisting_plugin(self, mos, mrequests):
|
||||
mos.path.exists.return_value = True
|
||||
mresponse = Mock(status_code=201)
|
||||
mresponse.json.return_value = [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'test',
|
||||
'version': '1.0.0',
|
||||
}
|
||||
]
|
||||
mrequests.get.return_value = mresponse
|
||||
|
||||
self.assertRaises(
|
||||
SystemExit,
|
||||
self.execute,
|
||||
['fuel', 'plugins', '--remove', 'test-fail']
|
||||
)
|
||||
|
||||
self.assertEqual(mrequests.delete.call_count, 0)
|
||||
|
||||
@patch('fuelclient.objects.plugins.os')
|
||||
def test_remove_when_multiple_versions(self, mos, mrequests):
|
||||
mos.path.exists.return_value = True
|
||||
mresponse = Mock(status_code=201)
|
||||
mresponse.json.return_value = [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'test',
|
||||
'version': '1.0.0',
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': 'test',
|
||||
'version': '1.1.0',
|
||||
}
|
||||
]
|
||||
mrequests.get.return_value = mresponse
|
||||
|
||||
self.assertRaises(
|
||||
SystemExit,
|
||||
self.execute,
|
||||
['fuel', 'plugins', '--remove', 'test']
|
||||
)
|
||||
|
||||
self.assertEqual(mrequests.delete.call_count, 0)
|
||||
|
||||
@patch('fuelclient.objects.plugins.os')
|
||||
def test_remove_nonexisting_plugin_version(self, mos, mrequests):
|
||||
mos.path.exists.return_value = True
|
||||
mresponse = Mock(status_code=201)
|
||||
mresponse.json.return_value = [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'test',
|
||||
'version': '1.0.0',
|
||||
}
|
||||
]
|
||||
mrequests.get.return_value = mresponse
|
||||
|
||||
self.assertRaises(
|
||||
SystemExit,
|
||||
self.execute,
|
||||
['fuel', 'plugins', '--remove', 'test==1.1.0']
|
||||
)
|
||||
|
||||
self.assertEqual(mrequests.delete.call_count, 0)
|
||||
|
||||
Reference in New Issue
Block a user