From 3ea38ccdb30c94068e5bb9ce39d673d78c8a7c48 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 19 Dec 2012 19:31:32 +0100 Subject: [PATCH] Split nested stack implementation into abstract and concrete Split the Stack resource class into an abstract part from which other resource types inherit and a concrete implementation of the AWS::CloudFormation::Stack resource type. Change-Id: Ie7600b94e17ec31b55bae5c4d9501d3f0cbca97c Signed-off-by: Zane Bitter --- heat/engine/resources/dbinstance.py | 2 +- heat/engine/resources/loadbalancer.py | 13 ++++----- heat/engine/resources/stack.py | 42 +++++++++++++-------------- heat/tests/test_dbinstance.py | 23 ++++++++------- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/heat/engine/resources/dbinstance.py b/heat/engine/resources/dbinstance.py index e2c1ea33..ea6a8633 100644 --- a/heat/engine/resources/dbinstance.py +++ b/heat/engine/resources/dbinstance.py @@ -220,7 +220,7 @@ class DBInstance(stack.Stack): def handle_create(self): templ = template_format.parse(mysql_template) - self.create_with_template(templ) + self.create_with_template(templ, self._params()) def FnGetAtt(self, key): ''' diff --git a/heat/engine/resources/loadbalancer.py b/heat/engine/resources/loadbalancer.py index 19ea37c6..bcb3cae1 100644 --- a/heat/engine/resources/loadbalancer.py +++ b/heat/engine/resources/loadbalancer.py @@ -200,12 +200,6 @@ class LoadBalancer(stack.Stack): 'Implemented': False} } - def _params(self): - # total hack - probably need an admin key here. - params = {'KeyName': {'Ref': 'KeyName'}} - p = self.stack.resolve_static_data(params) - return p - def _instance_to_ipaddress(self, inst): ''' Return the server's IP address, fetching it from Nova @@ -291,7 +285,10 @@ class LoadBalancer(stack.Stack): cfg = self._haproxy_config(templ, self.properties['Instances']) files['/etc/haproxy/haproxy.cfg']['content'] = cfg - self.create_with_template(templ) + # total hack - probably need an admin key here. + param = self.stack.resolve_static_data({'KeyName': {'Ref': 'KeyName'}}) + + self.create_with_template(templ, param) def validate(self): ''' @@ -343,7 +340,7 @@ class LoadBalancer(stack.Stack): key=key) if key == 'DNSName': - return stack.Stack.FnGetAtt(self, 'Outputs.PublicIp') + return self.get_output('PublicIp') else: return '' diff --git a/heat/engine/resources/stack.py b/heat/engine/resources/stack.py index e66e5b37..6d912eb7 100644 --- a/heat/engine/resources/stack.py +++ b/heat/engine/resources/stack.py @@ -30,19 +30,10 @@ logger = logging.getLogger(__name__) class Stack(resource.Resource): - properties_schema = {PROP_TEMPLATE_URL: {'Type': 'String', - 'Required': True}, - PROP_TIMEOUT_MINS: {'Type': 'Number'}, - PROP_PARAMETERS: {'Type': 'Map'}} - def __init__(self, name, json_snippet, stack): super(Stack, self).__init__(name, json_snippet, stack) self._nested = None - def _params(self): - p = self.stack.resolve_runtime_data(self.properties[PROP_PARAMETERS]) - return p - def nested(self): if self._nested is None and self.resource_id is not None: self._nested = parser.Stack.load(self.context, @@ -53,13 +44,13 @@ class Stack(resource.Resource): return self._nested - def create_with_template(self, child_template): + def create_with_template(self, child_template, user_params): ''' Handle the creation of the nested stack from a given JSON template. ''' template = parser.Template(child_template) params = parser.Parameters(self.physical_resource_name(), template, - self._params()) + user_params) self._nested = parser.Stack(self.context, self.physical_resource_name(), @@ -72,11 +63,26 @@ class Stack(resource.Resource): if self._nested.state != self._nested.CREATE_COMPLETE: raise exception.Error(self._nested.state_description) + def get_output(self, op): + stack = self.nested() + if op not in stack.outputs: + raise exception.InvalidTemplateAttribute( + resource=self.physical_resource_name(), key=key) + + return stack.output(op) + + +class NestedStack(Stack): + properties_schema = {PROP_TEMPLATE_URL: {'Type': 'String', + 'Required': True}, + PROP_TIMEOUT_MINS: {'Type': 'Number'}, + PROP_PARAMETERS: {'Type': 'Map'}} + def handle_create(self): template_data = urlfetch.get(self.properties[PROP_TEMPLATE_URL]) template = template_format.parse(template_data) - self.create_with_template(template) + self.create_with_template(template, self.properties[PROP_PARAMETERS]) def handle_update(self): return self.UPDATE_REPLACE @@ -96,18 +102,10 @@ class Stack(resource.Resource): resource=self.physical_resource_name(), key=key) prefix, dot, op = key.partition('.') - stack = self.nested() - if stack is None: - # This seems like a hack, to get past validation - return '' - if op not in stack.outputs: - raise exception.InvalidTemplateAttribute( - resource=self.physical_resource_name(), key=key) - - return stack.output(op) + return self.get_output(op) def resource_mapping(): return { - 'AWS::CloudFormation::Stack': Stack, + 'AWS::CloudFormation::Stack': NestedStack, } diff --git a/heat/tests/test_dbinstance.py b/heat/tests/test_dbinstance.py index 99d3c368..6aca59cd 100644 --- a/heat/tests/test_dbinstance.py +++ b/heat/tests/test_dbinstance.py @@ -79,7 +79,19 @@ class DBInstanceTest(unittest.TestCase): class FakeNested: resources = {'DatabaseInstance': FakeDatabaseInstance()} - stack.Stack.create_with_template(mox.IgnoreArg()).AndReturn(None) + params = { + 'AllocatedStorage': u'5', + 'DBInstanceClass': u'db.m1.small', + 'DBName': u'wordpress', + 'DBSecurityGroups': [], + 'KeyName': 'test', + 'MasterUserPassword': u'admin', + 'MasterUsername': u'admin', + 'Port': '3306' + } + + stack.Stack.create_with_template(mox.IgnoreArg(), + params).AndReturn(None) fn = FakeNested() @@ -91,15 +103,6 @@ class DBInstanceTest(unittest.TestCase): s = self.parse_stack(t) resource = self.create_dbinstance(t, s, 'DatabaseServer') - self.assertEqual({'AllocatedStorage': u'5', - 'DBInstanceClass': u'db.m1.small', - 'DBName': u'wordpress', - 'DBSecurityGroups': [], - 'KeyName': 'test', - 'MasterUserPassword': u'admin', - 'MasterUsername': u'admin', - 'Port': '3306'}, resource._params()) - self.assertEqual('0.0.0.0', resource.FnGetAtt('Endpoint.Address')) self.assertEqual('10.0.0.1', resource.FnGetAtt('Endpoint.Address')) self.assertEqual('3306', resource.FnGetAtt('Endpoint.Port')) -- 2.45.2