diff --git a/heat/engine/resources/autoscaling.py b/heat/engine/resources/autoscaling.py index 176b6608a..3d080ff45 100644 --- a/heat/engine/resources/autoscaling.py +++ b/heat/engine/resources/autoscaling.py @@ -116,7 +116,7 @@ class InstanceGroup(stack_resource.StackResource): ), LAUNCH_CONFIGURATION_NAME: properties.Schema( properties.Schema.STRING, - _('Name of LaunchConfiguration resource.'), + _('The reference to a LaunchConfiguration resource.'), required=True, update_allowed=True ), @@ -192,6 +192,28 @@ class InstanceGroup(stack_resource.StackResource): if iso8601utils.parse_isoduration(pause_time) > 3600: raise ValueError('Maximum PauseTime is 1 hour.') + def validate_launchconfig(self): + # It seems to be a common error to not have a dependancy on the + # launchconfiguration. This can happen if the the actual resource + # name is used instead of {get_resource: launch_conf} and no + # depends_on is used. + + conf_refid = self.properties.get(self.LAUNCH_CONFIGURATION_NAME) + if conf_refid: + conf = self.stack.resource_by_refid(conf_refid) + if conf is None: + raise ValueError(_('%(lc)s (%(ref)s)' + ' reference can not be found.') + % dict(lc=self.LAUNCH_CONFIGURATION_NAME, + ref=conf_refid)) + if self.name not in conf.required_by(): + raise ValueError(_('%(lc)s (%(ref)s)' + ' requires a reference to the' + ' configuration not just the name of the' + ' resource.') % dict( + lc=self.LAUNCH_CONFIGURATION_NAME, + ref=conf_refid)) + def get_instance_names(self): """Get a list of resource names of the instances in this InstanceGroup. @@ -221,6 +243,7 @@ class InstanceGroup(stack_resource.StackResource): def handle_create(self): """Create a nested stack and add the initial resources to it.""" + self.validate_launchconfig() num_instances = self.properties[self.SIZE] initial_template = self._create_template(num_instances) return self.create_with_template(initial_template, self._environment()) @@ -484,7 +507,7 @@ class AutoScalingGroup(InstanceGroup, cooldown.CooldownMixin): ), LAUNCH_CONFIGURATION_NAME: properties.Schema( properties.Schema.STRING, - _('Name of LaunchConfiguration resource.'), + _('The reference to a LaunchConfiguration resource.'), required=True, update_allowed=True ), @@ -574,6 +597,7 @@ class AutoScalingGroup(InstanceGroup, cooldown.CooldownMixin): } def handle_create(self): + self.validate_launchconfig() return self.create_with_template(self.child_template(), self._environment()) diff --git a/heat/tests/test_instance_group.py b/heat/tests/test_instance_group.py index f6976d776..21ced2ec3 100644 --- a/heat/tests/test_instance_group.py +++ b/heat/tests/test_instance_group.py @@ -12,6 +12,7 @@ # under the License. import copy +import six import mock @@ -364,6 +365,31 @@ class InstanceGroupTest(common.HeatTestCase): self.m.VerifyAll() + def test_validate_launch_conf(self): + t = template_format.parse(ig_template) + properties = t['Resources']['JobServerGroup']['Properties'] + properties['LaunchConfigurationName'] = 'urg_i_cant_spell' + stack = utils.parse_stack(t) + + rsrc = stack['JobServerGroup'] + creator = scheduler.TaskRunner(rsrc.create) + error = self.assertRaises(exception.ResourceFailure, creator) + + self.assertIn('(urg_i_cant_spell) reference can not be found.', + six.text_type(error)) + + def test_validate_launch_conf_no_ref(self): + t = template_format.parse(ig_template) + properties = t['Resources']['JobServerGroup']['Properties'] + properties['LaunchConfigurationName'] = 'JobServerConfig' + stack = utils.parse_stack(t) + + rsrc = stack['JobServerGroup'] + creator = scheduler.TaskRunner(rsrc.create) + error = self.assertRaises(exception.ResourceFailure, creator) + self.assertIn('(JobServerConfig) requires a reference to the', + six.text_type(error)) + class TestChildTemplate(common.HeatTestCase): def setUp(self):