Disallow disabling plugins which roles are used
This achieved by adding a new restriction for plugin-provided roles which prevents plugin to be disabled if env contains nodes with plugin-provided roles Closes-Bug: #1483633 Change-Id: I8e2502df2a9d84934730bc4681fc3c295fac450a
This commit is contained in:
parent
7903e1fc56
commit
60d4ddaaa4
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
|
||||
import abc
|
||||
import copy
|
||||
import glob
|
||||
import os
|
||||
|
||||
|
@ -205,6 +206,21 @@ class PluginAdapterBase(object):
|
|||
def volumes_metadata(self):
|
||||
return self.plugin.volumes_metadata
|
||||
|
||||
@property
|
||||
def normalized_roles_metadata(self):
|
||||
"""Adds a restriction for every role which blocks plugin disabling
|
||||
if nodes with plugin-provided roles exist in the cluster
|
||||
"""
|
||||
result = {}
|
||||
for role, meta in six.iteritems(self.plugin.roles_metadata):
|
||||
condition = "settings:{0}.metadata.enabled == false".format(
|
||||
self.plugin.name)
|
||||
meta = copy.copy(meta)
|
||||
meta['restrictions'] = [condition] + meta.get('restrictions', [])
|
||||
result[role] = meta
|
||||
|
||||
return result
|
||||
|
||||
def get_release_info(self, release):
|
||||
"""Returns plugin release information which corresponds to
|
||||
a provided release.
|
||||
|
|
|
@ -138,7 +138,7 @@ class PluginManager(object):
|
|||
core_roles = set(cluster.release.roles_metadata)
|
||||
|
||||
for plugin_db in cluster.plugins:
|
||||
plugin_roles = plugin_db.roles_metadata
|
||||
plugin_roles = wrap_plugin(plugin_db).normalized_roles_metadata
|
||||
|
||||
# we should check all possible cases of roles intersection
|
||||
# with core ones and those from other plugins
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
import yaml
|
||||
|
||||
from nailgun import objects
|
||||
from nailgun.plugins import adapters
|
||||
from nailgun.test import base
|
||||
|
||||
|
||||
|
@ -162,6 +163,7 @@ class TestClusterRolesHandler(base.BaseTestCase):
|
|||
plugin = objects.Plugin.create(plugin_data)
|
||||
self.cluster.plugins.append(plugin)
|
||||
self.db.flush()
|
||||
plugin_adapter = adapters.wrap_plugin(plugin)
|
||||
|
||||
role = self.app.get(
|
||||
url=base.reverse(
|
||||
|
@ -173,8 +175,10 @@ class TestClusterRolesHandler(base.BaseTestCase):
|
|||
self.assertEqual(role['name'], 'test_role')
|
||||
self.assertDictEqual(
|
||||
role['meta'],
|
||||
self.ROLES['test_role']
|
||||
plugin_adapter.normalized_roles_metadata['test_role']
|
||||
)
|
||||
self.assertItemsEqual(
|
||||
role['volumes_roles_mapping'],
|
||||
self.VOLUMES['volumes_roles_mapping']['test_role'])
|
||||
plugin_adapter.volumes_metadata[
|
||||
'volumes_roles_mapping']['test_role']
|
||||
)
|
||||
|
|
|
@ -1085,12 +1085,7 @@ class TestClusterObjectGetRoles(BaseTestCase):
|
|||
|
||||
def test_no_plugins_no_additional_roles(self):
|
||||
roles = objects.Cluster.get_roles(self.cluster)
|
||||
self.assertEqual(roles, {
|
||||
'role_a': {
|
||||
'name': 'Role A', 'description': 'Role A is ...', },
|
||||
'role_b': {
|
||||
'name': 'Role B', 'description': 'Role B is ...', },
|
||||
})
|
||||
self.assertItemsEqual(roles.keys(), ['role_a', 'role_b'])
|
||||
|
||||
def test_plugin_adds_new_roles(self):
|
||||
self.create_plugin({
|
||||
|
@ -1099,14 +1094,7 @@ class TestClusterObjectGetRoles(BaseTestCase):
|
|||
})
|
||||
|
||||
roles = objects.Cluster.get_roles(self.cluster)
|
||||
self.assertEqual(roles, {
|
||||
'role_a': {
|
||||
'name': 'Role A', 'description': 'Role A is ...', },
|
||||
'role_b': {
|
||||
'name': 'Role B', 'description': 'Role B is ...', },
|
||||
'role_c': {
|
||||
'name': 'Role C', 'description': 'Role C is ...', },
|
||||
})
|
||||
self.assertItemsEqual(roles.keys(), ['role_a', 'role_b', 'role_c'])
|
||||
|
||||
def test_plugin_role_conflict_with_core_roles(self):
|
||||
plugin = self.create_plugin({
|
||||
|
@ -1164,9 +1152,9 @@ class TestClusterObjectGetRoles(BaseTestCase):
|
|||
|
||||
# 0 - the whole message, 1 - is first match of (.*) pattern
|
||||
roles = re.match(message_pattern, str(cm.exception)).group(1)
|
||||
roles = set([role.lstrip().rstrip() for role in roles.split(',')])
|
||||
roles = [role.lstrip().rstrip() for role in roles.split(',')]
|
||||
|
||||
self.assertEqual(roles, set(['role_x', 'role_a']))
|
||||
self.assertItemsEqual(roles, ['role_x', 'role_a'])
|
||||
|
||||
|
||||
class TestClusterObjectGetNetworkManager(BaseTestCase):
|
||||
|
|
|
@ -20,6 +20,8 @@ import six
|
|||
import yaml
|
||||
|
||||
from nailgun.db import db
|
||||
from nailgun.errors import errors
|
||||
from nailgun.expression import Expression
|
||||
from nailgun.objects import Plugin
|
||||
from nailgun.plugins import adapters
|
||||
from nailgun.settings import settings
|
||||
|
@ -37,7 +39,26 @@ class TestPluginBase(base.BaseTestCase):
|
|||
def setUp(self):
|
||||
super(TestPluginBase, self).setUp()
|
||||
self.plugin_metadata = self.env.get_default_plugin_metadata(
|
||||
package_version=self.package_version)
|
||||
package_version=self.package_version,
|
||||
roles_metadata={
|
||||
'role_x': {
|
||||
'name': 'Role X',
|
||||
'description': 'Role X is ...',
|
||||
},
|
||||
'role_y': {
|
||||
'name': 'Role Y',
|
||||
'description': 'Role Y is ...',
|
||||
'restrictions': []
|
||||
},
|
||||
'role_z': {
|
||||
'name': 'Role Z',
|
||||
'description': 'Role Z is ...',
|
||||
'restrictions': [
|
||||
'settings:some.stuff.value == false'
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
self.plugin = Plugin.create(self.plugin_metadata)
|
||||
self.env.create(
|
||||
cluster_kwargs={'mode': 'multinode'},
|
||||
|
@ -92,6 +113,20 @@ class TestPluginBase(base.BaseTestCase):
|
|||
release = self.plugin_adapter.get_release_info(self.cluster.release)
|
||||
self.assertEqual(release, self.plugin_metadata['releases'][0])
|
||||
|
||||
def test_plugin_role_restrictions_normalization(self):
|
||||
# checking presence and syntax of generated restriction
|
||||
for role, meta in six.iteritems(
|
||||
self.plugin_adapter.normalized_roles_metadata):
|
||||
for condition in meta['restrictions']:
|
||||
self.assertNotRaises(
|
||||
errors.ParseError,
|
||||
lambda: Expression(
|
||||
condition,
|
||||
{'settings': self.cluster.attributes.editable},
|
||||
strict=False
|
||||
).evaluate()
|
||||
)
|
||||
|
||||
def test_slaves_scripts_path(self):
|
||||
expected = settings.PLUGINS_SLAVES_SCRIPTS_PATH.format(
|
||||
plugin_name=self.plugin_adapter.path_name)
|
||||
|
|
Loading…
Reference in New Issue