In case of several plugins, nailgun should use the newest
For example Fuel has 3 plugins: - name: plugin_name, version: 1.0.0 - name: plugin_name, version: 2.0.0 - name: plugin_name, version: 0.1.0 The problem was, when user enables a plugin on UI, Nailgun enables all of the installed plugins. With this patch, after user creates environment he can enable or disable only 2.0.0 version of plugin. If after environment creation, new version of the plugin is installed, user cannot use this new version, he should create new environment in order to enable it. Change-Id: I51ba03166cd2a7379c6f0912d668265bf88a316d Closes-bug: #1395342
This commit is contained in:
parent
e4c59607c8
commit
00dcb89989
|
@ -14,12 +14,14 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from itertools import groupby
|
||||
|
||||
from nailgun.db import db
|
||||
from nailgun.db.sqlalchemy.models import plugins as plugin_db_model
|
||||
from nailgun.objects import base
|
||||
from nailgun.objects.serializers import plugin
|
||||
|
||||
from nailgun.db import db
|
||||
|
||||
|
||||
class Plugin(base.NailgunObject):
|
||||
|
||||
|
@ -35,3 +37,30 @@ class Plugin(base.NailgunObject):
|
|||
class PluginCollection(base.NailgunCollection):
|
||||
|
||||
single = Plugin
|
||||
|
||||
@classmethod
|
||||
def all_newest(cls):
|
||||
"""Returns new plugins.
|
||||
Example:
|
||||
There are 4 plugins:
|
||||
- name: plugin_name, version: 1.0.0
|
||||
- name: plugin_name, version: 2.0.0
|
||||
- name: plugin_name, version: 0.1.0
|
||||
- name: plugin_another_name, version: 1.0.0
|
||||
In this case the method returns 2 plugins:
|
||||
- name: plugin_name, version: 2.0.0
|
||||
- name: plugin_another_name, version: 1.0.0
|
||||
|
||||
:returns: list of Plugin models
|
||||
"""
|
||||
newest_plugins = []
|
||||
grouped_by_name = groupby(cls.all(), lambda p: p.name)
|
||||
for name, plugins in grouped_by_name:
|
||||
newest_plugin = sorted(
|
||||
plugins,
|
||||
key=lambda p: LooseVersion(p.version),
|
||||
reverse=True)[0]
|
||||
|
||||
newest_plugins.append(newest_plugin)
|
||||
|
||||
return newest_plugins
|
||||
|
|
|
@ -96,7 +96,13 @@ class ClusterAttributesPlugin(object):
|
|||
enabled field.
|
||||
"""
|
||||
custom_attrs = cluster_attrs.get(self.plugin.name, {})
|
||||
|
||||
if custom_attrs:
|
||||
# Skip if it's wrong plugin version
|
||||
attr_plugin_version = custom_attrs['metadata']['plugin_version']
|
||||
if attr_plugin_version != self.plugin.version:
|
||||
return
|
||||
|
||||
enable = custom_attrs['metadata']['enabled']
|
||||
# value is true and plugin is not enabled for this cluster
|
||||
# that means plugin was enabled on this request
|
||||
|
@ -119,7 +125,8 @@ class ClusterAttributesPlugin(object):
|
|||
@property
|
||||
def default_metadata(self):
|
||||
return {u'enabled': False, u'toggleable': True,
|
||||
u'weight': 70, u'label': self.plugin.title}
|
||||
u'weight': 70, u'label': self.plugin.title,
|
||||
'plugin_version': self.plugin.version}
|
||||
|
||||
def set_cluster_tasks(self, cluster):
|
||||
"""Loads plugins provided tasks from tasks config file and
|
||||
|
|
|
@ -30,7 +30,7 @@ class PluginManager(object):
|
|||
@classmethod
|
||||
def get_plugin_attributes(cls, cluster):
|
||||
plugins_attrs = {}
|
||||
for plugin_db in PluginCollection.all():
|
||||
for plugin_db in PluginCollection.all_newest():
|
||||
attr_plugin = ClusterAttributesPlugin(plugin_db)
|
||||
attrs = attr_plugin.get_plugin_attributes(cluster)
|
||||
plugins_attrs.update(attrs)
|
||||
|
|
|
@ -438,6 +438,37 @@ class Environment(object):
|
|||
return self.read_fixtures(
|
||||
['openstack'])[0]['fields']['attributes_metadata']
|
||||
|
||||
def get_default_plugin_env_config(self, **kwargs):
|
||||
return {
|
||||
'attributes': {
|
||||
'{0}_text'.format(kwargs.get('plugin_name', 'plugin_name')): {
|
||||
'value': kwargs.get('value', 'value'),
|
||||
'type': kwargs.get('type', 'type'),
|
||||
'description': kwargs.get('description', 'description'),
|
||||
'weight': kwargs.get('weight', 25),
|
||||
'label': kwargs.get('label', 'label')}}}
|
||||
|
||||
def get_default_plugin_metadata(self, **kwargs):
|
||||
sample_plugin = {
|
||||
'version': '0.1.0',
|
||||
'name': 'testing_plugin',
|
||||
'title': 'Test plugin',
|
||||
'package_version': '1.0.0',
|
||||
'description': 'Enable to use plugin X for Neutron',
|
||||
'fuel_version': ['6.0'],
|
||||
'releases': [
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': '2014.2-6.0', 'os': 'ubuntu',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/centos',
|
||||
'version': '2014.2-6.0', 'os': 'centos',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}]}
|
||||
|
||||
sample_plugin.update(kwargs)
|
||||
return sample_plugin
|
||||
|
||||
def upload_fixtures(self, fxtr_names):
|
||||
for fxtr_path in self.fxtr_paths_by_names(fxtr_names):
|
||||
with open(fxtr_path, "r") as fxtr_file:
|
||||
|
|
|
@ -32,33 +32,6 @@ def get_config(config):
|
|||
|
||||
class BasePluginTest(base.BaseIntegrationTest):
|
||||
|
||||
SAMPLE_PLUGIN = {
|
||||
'version': '1.1.0',
|
||||
'name': 'testing',
|
||||
'title': 'Test plugin',
|
||||
'package_version': '1.0.0',
|
||||
'description': 'Enable to use plugin X for Neutron',
|
||||
'fuel_version': ["6.0"],
|
||||
'releases': [
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': '2014.2-6.0',
|
||||
'os': 'ubuntu',
|
||||
'mode': ['ha_compact', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/centos',
|
||||
'version': '2014.2-6.0', 'os': 'centos',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}]}
|
||||
|
||||
ENVIRONMENT_CONFIG = {
|
||||
'attributes': {
|
||||
'lbaas_simple_text': {
|
||||
'value': 'Set default value',
|
||||
'type': 'text',
|
||||
'description': 'Description for text field',
|
||||
'weight': 25,
|
||||
'label': 'Text field'}}}
|
||||
|
||||
TASKS_CONFIG = [
|
||||
{'priority': 10,
|
||||
'role': ['controller'],
|
||||
|
@ -71,8 +44,13 @@ class BasePluginTest(base.BaseIntegrationTest):
|
|||
'parameters': {'cmd': 'echo all > /tmp/plugin.all', 'timeout': 42},
|
||||
'stage': 'pre_deployment'}]
|
||||
|
||||
def setUp(self):
|
||||
super(BasePluginTest, self).setUp()
|
||||
self.sample_plugin = self.env.get_default_plugin_metadata()
|
||||
self.plugin_env_config = self.env.get_default_plugin_env_config()
|
||||
|
||||
def create_plugin(self, sample=None, expect_errors=False):
|
||||
sample = sample or self.SAMPLE_PLUGIN
|
||||
sample = sample or self.sample_plugin
|
||||
resp = self.app.post(
|
||||
base.reverse('PluginCollectionHandler'),
|
||||
jsonutils.dumps(sample),
|
||||
|
@ -95,7 +73,7 @@ class BasePluginTest(base.BaseIntegrationTest):
|
|||
create=True) as f_m:
|
||||
os.access.return_value = True
|
||||
os.path.exists.return_value = True
|
||||
f_m.side_effect = get_config(self.ENVIRONMENT_CONFIG)
|
||||
f_m.side_effect = get_config(self.plugin_env_config)
|
||||
self.env.create(
|
||||
release_kwargs={'version': '2014.2-6.0',
|
||||
'operating_system': 'Ubuntu'},
|
||||
|
@ -163,7 +141,7 @@ class TestPluginsApi(BasePluginTest):
|
|||
def test_env_create_and_load_env_config(self):
|
||||
self.create_plugin()
|
||||
cluster = self.create_cluster()
|
||||
self.assertIn(self.SAMPLE_PLUGIN['name'], cluster.attributes.editable)
|
||||
self.assertIn(self.sample_plugin['name'], cluster.attributes.editable)
|
||||
|
||||
def test_enable_disable_plugin(self):
|
||||
resp = self.create_plugin()
|
||||
|
@ -201,7 +179,29 @@ class TestPluginsApi(BasePluginTest):
|
|||
self.create_plugin()
|
||||
cluster = self.create_cluster()
|
||||
default_attributes = self.default_attributes(cluster)
|
||||
self.assertIn(self.SAMPLE_PLUGIN['name'], default_attributes)
|
||||
self.assertIn(self.sample_plugin['name'], default_attributes)
|
||||
|
||||
def test_plugins_multiversioning(self):
|
||||
def create_with_version(version):
|
||||
self.create_plugin(sample=self.env.get_default_plugin_metadata(
|
||||
name='multiversion_plugin', version=version))
|
||||
|
||||
for version in ['1.0.0', '2.0.0', '0.0.1']:
|
||||
create_with_version(version)
|
||||
|
||||
cluster = self.create_cluster()
|
||||
# Create new plugin after environment is created
|
||||
create_with_version('5.0.0')
|
||||
|
||||
self.enable_plugin(cluster, 'multiversion_plugin')
|
||||
self.assertEqual(len(cluster.plugins), 1)
|
||||
enabled_plugin = cluster.plugins[0]
|
||||
# Should be enabled the newest plugin,
|
||||
# at the moment of environment creation
|
||||
self.assertEqual(enabled_plugin.version, '2.0.0')
|
||||
|
||||
self.disable_plugin(cluster, 'multiversion_plugin')
|
||||
self.assertEqual(len(cluster.plugins), 0)
|
||||
|
||||
|
||||
class TestPrePostHooks(BasePluginTest):
|
||||
|
@ -214,7 +214,7 @@ class TestPrePostHooks(BasePluginTest):
|
|||
self.cluster = self.create_cluster([
|
||||
{'roles': ['controller'], 'pending_addition': True},
|
||||
{'roles': ['compute'], 'pending_addition': True}])
|
||||
self.enable_plugin(self.cluster, self.SAMPLE_PLUGIN['name'])
|
||||
self.enable_plugin(self.cluster, self.sample_plugin['name'])
|
||||
|
||||
def test_generate_pre_hooks(self):
|
||||
tasks = self.get_pre_hooks(self.cluster).json
|
||||
|
|
|
@ -25,48 +25,22 @@ from nailgun.settings import settings
|
|||
from nailgun.test import base
|
||||
|
||||
|
||||
SAMPLE_PLUGIN = {
|
||||
'version': '0.1.0',
|
||||
'name': 'testing_plugin',
|
||||
'title': 'Test plugin',
|
||||
'package_version': '1.0.0',
|
||||
'description': 'Enable to use plugin X for Neutron',
|
||||
'fuel_version': ['6.0'],
|
||||
'releases': [
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': '2014.2-6.0', 'os': 'ubuntu',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/centos',
|
||||
'version': '2014.2-6.0', 'os': 'centos',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}]}
|
||||
|
||||
ENVIRONMENT_CONFIG = {
|
||||
'attributes': {
|
||||
'lbaas_simple_text': {
|
||||
'value': 'Set default value',
|
||||
'type': 'text',
|
||||
'description': 'Description for text field',
|
||||
'weight': 25,
|
||||
'label': 'Text field'}}}
|
||||
|
||||
|
||||
def get_config(*args):
|
||||
return mock.mock_open(read_data=yaml.dump(ENVIRONMENT_CONFIG))()
|
||||
|
||||
|
||||
class TestPlugin(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPlugin, self).setUp()
|
||||
self.plugin = Plugin.create(SAMPLE_PLUGIN)
|
||||
self.plugin_metadata = self.env.get_default_plugin_metadata()
|
||||
self.plugin = Plugin.create(self.plugin_metadata)
|
||||
self.env.create(
|
||||
cluster_kwargs={'mode': 'multinode'},
|
||||
release_kwargs={'version': '2014.2-6.0',
|
||||
'operating_system': 'Ubuntu'})
|
||||
self.cluster = self.env.clusters[0]
|
||||
self.attr_plugin = attr_plugin.ClusterAttributesPlugin(self.plugin)
|
||||
self.env_config = self.env.get_default_plugin_env_config()
|
||||
self.get_config = lambda *args: mock.mock_open(
|
||||
read_data=yaml.dump(self.env_config))()
|
||||
|
||||
db().flush()
|
||||
|
||||
@mock.patch('nailgun.plugins.attr_plugin.open', create=True)
|
||||
|
@ -79,12 +53,11 @@ class TestPlugin(base.BaseTestCase):
|
|||
"""
|
||||
maccess.return_value = True
|
||||
mexists.return_value = True
|
||||
mopen.side_effect = get_config
|
||||
attributes = self.attr_plugin.get_plugin_attributes(
|
||||
self.cluster)
|
||||
mopen.side_effect = self.get_config
|
||||
attributes = self.attr_plugin.get_plugin_attributes(self.cluster)
|
||||
self.assertEqual(
|
||||
attributes['testing_plugin']['lbaas_simple_text'],
|
||||
ENVIRONMENT_CONFIG['attributes']['lbaas_simple_text'])
|
||||
attributes['testing_plugin']['plugin_name_text'],
|
||||
self.env_config['attributes']['plugin_name_text'])
|
||||
self.assertEqual(
|
||||
attributes['testing_plugin']['metadata'],
|
||||
self.attr_plugin.default_metadata)
|
||||
|
@ -107,7 +80,7 @@ class TestPlugin(base.BaseTestCase):
|
|||
provided release.
|
||||
"""
|
||||
release = self.attr_plugin.get_release_info(self.cluster.release)
|
||||
self.assertEqual(release, SAMPLE_PLUGIN['releases'][0])
|
||||
self.assertEqual(release, self.plugin_metadata['releases'][0])
|
||||
|
||||
def test_slaves_scripts_path(self):
|
||||
expected = settings.PLUGINS_SLAVES_SCRIPTS_PATH.format(
|
||||
|
@ -145,7 +118,7 @@ class TestClusterCompatiblityValidation(base.BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(TestClusterCompatiblityValidation, self).setUp()
|
||||
self.plugin = Plugin.create(SAMPLE_PLUGIN)
|
||||
self.plugin = Plugin.create(self.env.get_default_plugin_metadata())
|
||||
self.attr_plugin = attr_plugin.ClusterAttributesPlugin(self.plugin)
|
||||
|
||||
def get_cluster(self, os, mode, version):
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2014 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from nailgun.objects import Plugin
|
||||
from nailgun.objects import PluginCollection
|
||||
from nailgun.test import base
|
||||
|
||||
|
||||
class TestPluginCollection(base.BaseTestCase):
|
||||
|
||||
def test_all_newest(self):
|
||||
for version in ['1.0.0', '2.0.0', '0.0.1']:
|
||||
plugin_data = self.env.get_default_plugin_metadata(
|
||||
version=version,
|
||||
name='multiversion_plugin')
|
||||
Plugin.create(plugin_data)
|
||||
|
||||
single_plugin_data = self.env.get_default_plugin_metadata(
|
||||
name='single_plugin')
|
||||
Plugin.create(single_plugin_data)
|
||||
|
||||
newest_plugins = PluginCollection.all_newest()
|
||||
self.assertEqual(len(newest_plugins), 2)
|
||||
|
||||
single_plugin = filter(
|
||||
lambda p: p.name == 'single_plugin',
|
||||
newest_plugins)
|
||||
multiversion_plugin = filter(
|
||||
lambda p: p.name == 'multiversion_plugin',
|
||||
newest_plugins)
|
||||
|
||||
self.assertEqual(len(single_plugin), 1)
|
||||
self.assertEqual(len(multiversion_plugin), 1)
|
||||
|
||||
self.assertEqual(multiversion_plugin[0].version, '2.0.0')
|
Loading…
Reference in New Issue