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:
Vitaly Kramskikh 2015-08-11 19:55:39 +03:00
parent 7903e1fc56
commit 60d4ddaaa4
5 changed files with 63 additions and 20 deletions

View File

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

View File

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

View File

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

View File

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

View File

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