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:
Przemyslaw Kaminski
2015-01-13 17:04:03 +01:00
parent 1f3ba542dd
commit 2215cd5662
4 changed files with 210 additions and 2 deletions

View File

@@ -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))

View File

@@ -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
)

View File

@@ -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

View File

@@ -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)