diff --git a/savanna/service/validations/base.py b/savanna/service/validations/base.py index 6f0b5e253d..9537d92403 100644 --- a/savanna/service/validations/base.py +++ b/savanna/service/validations/base.py @@ -217,3 +217,19 @@ def check_cinder_exists(): keystone.client().services.list()] if 'cinder' not in services: raise ex.InvalidException("Cinder is not supported") + + +##Tags + + +def check_required_image_tags(plugin_name, hadoop_version, image_id): + image = api.get_image(id=image_id) + plugin = plugin_base.PLUGINS.get_plugin(plugin_name) + req_tags = set(plugin.get_required_image_tags(hadoop_version)) + if not req_tags.issubset(set(image.tags)): + raise ex.InvalidException("Tags of requested image '%s' don't " + "contain required tags " + "['%s', '%s']" % + (image_id, + plugin_name, + hadoop_version)) diff --git a/savanna/service/validations/cluster_templates.py b/savanna/service/validations/cluster_templates.py index eee28006cf..32f2685874 100644 --- a/savanna/service/validations/cluster_templates.py +++ b/savanna/service/validations/cluster_templates.py @@ -106,6 +106,9 @@ def check_cluster_template_create(data, **kwargs): if data.get('default_image_id'): b.check_image_registered(data['default_image_id']) + b.check_required_image_tags(data['plugin_name'], + data['hadoop_version'], + data['default_image_id']) b.check_all_configurations(data) diff --git a/savanna/service/validations/clusters.py b/savanna/service/validations/clusters.py index 629b5da008..346b5709a7 100644 --- a/savanna/service/validations/clusters.py +++ b/savanna/service/validations/clusters.py @@ -49,6 +49,9 @@ def check_cluster_create(data, **kwargs): if data.get('default_image_id'): b.check_image_registered(data['default_image_id']) + b.check_required_image_tags(data['plugin_name'], + data['hadoop_version'], + data['default_image_id']) b.check_all_configurations(data) diff --git a/savanna/tests/unit/service/validation/test_cluster_create_validation.py b/savanna/tests/unit/service/validation/test_cluster_create_validation.py index 3af973d29b..de21a4249e 100644 --- a/savanna/tests/unit/service/validation/test_cluster_create_validation.py +++ b/savanna/tests/unit/service/validation/test_cluster_create_validation.py @@ -162,3 +162,6 @@ class TestClusterCreateValidation(u.ValidationTestCase): 'default_image_id': '550e8400-e29b-41d4-a716-446655440000' } ) + + def test_cluster_create_v_default_image_required_tags(self): + self._assert_cluster_default_image_tags_validation() diff --git a/savanna/tests/unit/service/validation/test_cluster_template_create_validation.py b/savanna/tests/unit/service/validation/test_cluster_template_create_validation.py index 26aef98c3f..1d1cd0ca1b 100644 --- a/savanna/tests/unit/service/validation/test_cluster_template_create_validation.py +++ b/savanna/tests/unit/service/validation/test_cluster_template_create_validation.py @@ -200,3 +200,6 @@ class TestClusterTemplateCreateValidation(u.ValidationTestCase): bad_req_i=(1, 'NAME_ALREADY_EXISTS', "Cluster template with name 'test' already exists") ) + + def test_cluster_create_v_default_image_required_tags(self): + self._assert_cluster_default_image_tags_validation() diff --git a/savanna/tests/unit/service/validation/utils.py b/savanna/tests/unit/service/validation/utils.py index ab19904e29..04d7620858 100644 --- a/savanna/tests/unit/service/validation/utils.py +++ b/savanna/tests/unit/service/validation/utils.py @@ -74,7 +74,9 @@ def start_patch(): mock.patch("savanna.service.api.get_cluster_template") nova_p = mock.patch("savanna.utils.openstack.nova.client") keystone_p = mock.patch("savanna.utils.openstack.keystone.client") + get_image_p = mock.patch("savanna.service.api.get_image") + get_image = get_image_p.start() get_clusters = get_clusters_p.start() get_cluster = get_cluster_p.start() get_ng_templates = get_ng_templates_p.start() @@ -103,11 +105,32 @@ def start_patch(): keystone().services.list.side_effect = _services_list class Image: + def __init__(self, name='test'): + self.name = name + @property def id(self): - return '550e8400-e29b-41d4-a716-446655440000' + if self.name == 'test': + return '550e8400-e29b-41d4-a716-446655440000' + else: + return '813fe450-40d2-4acc-ade5-ea753a1bd5bc' - nova().images.list_registered.return_value = [Image()] + @property + def tags(self): + if self.name == 'test': + return ['vanilla', '1.1.2'] + else: + return ['wrong_tag'] + + def _get_image(id): + if id == '550e8400-e29b-41d4-a716-446655440000': + return Image() + else: + return Image('wrong_test') + + get_image.side_effect = _get_image + nova().images.list_registered.return_value = [Image(), + Image(name='wrong_name')] cluster = m.Cluster('test', 't', 'vanilla', '1.2.2') cluster.id = 1 cluster.status = 'Active' @@ -143,7 +166,8 @@ def start_patch(): # request data to validate patchers = (get_clusters_p, get_ng_templates_p, get_ng_template_p, get_plugins_p, get_plugin_p, - get_cl_template_p, get_cl_templates_p, nova_p, keystone_p) + get_cl_template_p, get_cl_templates_p, nova_p, keystone_p, + get_image_p) return patchers @@ -278,3 +302,25 @@ class ValidationTestCase(unittest2.TestCase): "Plugin's applicable target 'HDFS' doesn't " "contain config with name 's'") ) + + def _assert_cluster_default_image_tags_validation(self): + data = { + 'name': 'test-cluster', + 'plugin_name': 'vanilla', + 'hadoop_version': '1.1.2', + 'default_image_id': '550e8400-e29b-41d4-a716-446655440000' + } + self._assert_create_object_validation(data=data) + data = { + 'name': 'test-cluster', + 'plugin_name': 'vanilla', + 'hadoop_version': '1.1.2', + 'default_image_id': '813fe450-40d2-4acc-ade5-ea753a1bd5bc' + } + self._assert_create_object_validation( + data=data, + bad_req_i=(1, 'INVALID_REFERENCE', + "Tags of requested image " + "'813fe450-40d2-4acc-ade5-ea753a1bd5bc' " + "don't contain required tags " + "['vanilla', '1.1.2']"))