Merge "Fix stack-show on a TemplateResource with outputs"
This commit is contained in:
commit
3110364647
|
@ -13,6 +13,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
from requests import exceptions
|
||||
|
||||
from heat.common import exception
|
||||
|
@ -43,6 +44,7 @@ class TemplateResource(stack_resource.StackResource):
|
|||
self._parsed_nested = None
|
||||
self.stack = stack
|
||||
self.validation_exception = None
|
||||
self.update_allowed_keys = ('Properties',)
|
||||
|
||||
tri = stack.env.get_resource_info(
|
||||
json_snippet['Type'],
|
||||
|
@ -51,33 +53,41 @@ class TemplateResource(stack_resource.StackResource):
|
|||
self.validation_exception = ValueError(_(
|
||||
'Only Templates with an extension of .yaml or '
|
||||
'.template are supported'))
|
||||
self.properties_schema = {}
|
||||
self.attributes_schema = {}
|
||||
super(TemplateResource, self).__init__(name, json_snippet, stack)
|
||||
return
|
||||
|
||||
self.template_name = tri.template_name
|
||||
if tri.user_resource:
|
||||
self.allowed_schemes = ('http', 'https')
|
||||
else:
|
||||
self.allowed_schemes = ('http', 'https', 'file')
|
||||
self.template_name = tri.template_name
|
||||
if tri.user_resource:
|
||||
self.allowed_schemes = ('http', 'https')
|
||||
else:
|
||||
self.allowed_schemes = ('http', 'https', 'file')
|
||||
|
||||
# parse_nested can fail if the URL in the environment is bad
|
||||
# or otherwise inaccessible. Suppress the error here so the
|
||||
# stack can be deleted, and detect it at validate/create time
|
||||
# run Resource.__init__() so we can call self.nested()
|
||||
self.properties_schema = {}
|
||||
self.attributes_schema = {}
|
||||
super(TemplateResource, self).__init__(name, json_snippet, stack)
|
||||
if self.validation_exception is None:
|
||||
self._generate_schema(self.t.get('Properties', {}))
|
||||
|
||||
def _generate_schema(self, props):
|
||||
self._parsed_nested = None
|
||||
try:
|
||||
tmpl = template.Template(self.parsed_nested())
|
||||
except ValueError as parse_error:
|
||||
self.validation_exception = parse_error
|
||||
except ValueError as download_error:
|
||||
self.validation_exception = download_error
|
||||
tmpl = template.Template({})
|
||||
|
||||
# re-generate the properties and attributes from the template.
|
||||
self.properties_schema = (properties.Properties
|
||||
.schema_from_params(tmpl.param_schemata()))
|
||||
.schema_from_params(tmpl.param_schemata()))
|
||||
self.attributes_schema = (attributes.Attributes
|
||||
.schema_from_outputs(tmpl[template.OUTPUTS]))
|
||||
self.update_allowed_keys = ('Properties',)
|
||||
.schema_from_outputs(tmpl[template.OUTPUTS]))
|
||||
|
||||
super(TemplateResource, self).__init__(name, json_snippet, stack)
|
||||
self.properties = properties.Properties(self.properties_schema,
|
||||
props,
|
||||
self._resolve_runtime_data,
|
||||
self.name)
|
||||
self.attributes = attributes.Attributes(self.name,
|
||||
self.attributes_schema,
|
||||
self._resolve_attribute)
|
||||
|
||||
def _to_parameters(self):
|
||||
'''
|
||||
|
@ -117,22 +127,33 @@ class TemplateResource(stack_resource.StackResource):
|
|||
return self._parsed_nested
|
||||
|
||||
def template_data(self):
|
||||
# we want to have the latest possible template.
|
||||
# 1. look in files
|
||||
# 2. try download
|
||||
# 3. look in the db
|
||||
reported_excp = None
|
||||
t_data = self.stack.t.files.get(self.template_name)
|
||||
if not t_data and self.template_name.endswith((".yaml", ".template")):
|
||||
try:
|
||||
t_data = urlfetch.get(self.template_name,
|
||||
allowed_schemes=self.allowed_schemes)
|
||||
except (exceptions.RequestException, IOError) as r_exc:
|
||||
raise ValueError(_("Could not fetch remote template "
|
||||
"'%(name)s': %(exc)s") % {
|
||||
'name': self.template_name,
|
||||
'exc': str(r_exc)})
|
||||
else:
|
||||
# TODO(Randall) Whoops, misunderstanding on my part; this
|
||||
# doesn't actually persist to the db like I thought.
|
||||
# Find a better way
|
||||
self.stack.t.files[self.template_name] = t_data
|
||||
return t_data
|
||||
reported_excp = ValueError(_("Could not fetch remote template "
|
||||
"'%(name)s': %(exc)s") % {
|
||||
'name': self.template_name,
|
||||
'exc': str(r_exc)})
|
||||
|
||||
if t_data is None:
|
||||
if self.nested() is not None:
|
||||
t_data = json.dumps(self.nested().t.t)
|
||||
|
||||
if t_data is not None:
|
||||
self.stack.t.files[self.template_name] = t_data
|
||||
return t_data
|
||||
if reported_excp is None:
|
||||
reported_excp = ValueError(_('Unknown error retrieving %s') %
|
||||
self.template_name)
|
||||
raise reported_excp
|
||||
|
||||
def _validate_against_facade(self, facade_cls):
|
||||
facade_schemata = properties.schemata(facade_cls.properties_schema)
|
||||
|
|
|
@ -517,6 +517,60 @@ class ProviderTemplateTest(HeatTestCase):
|
|||
self.assertRaises(exception.StackValidationFailed, temp_res.validate)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def create_file_based_template_resource(self):
|
||||
test_template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Resources:
|
||||
the_nested:
|
||||
Type: the.yaml
|
||||
Properties:
|
||||
one: myname
|
||||
'''
|
||||
|
||||
resource_template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Parameters:
|
||||
one:
|
||||
Type: String
|
||||
Resources:
|
||||
NestedResource:
|
||||
Type: GenericResource
|
||||
Outputs:
|
||||
Foo:
|
||||
Value: {Ref: one}
|
||||
'''
|
||||
utils.setup_dummy_db()
|
||||
self.ctx = utils.dummy_context('test_username', 'aaaa', 'password')
|
||||
resource._register_class('GenericResource',
|
||||
generic_rsrc.GenericResource)
|
||||
|
||||
tmpl = parser.Template(template_format.parse(test_template),
|
||||
files={'the.yaml': resource_template})
|
||||
self.stack = parser.Stack(self.ctx, utils.random_name(), tmpl)
|
||||
self.stack.store()
|
||||
self.stack.create()
|
||||
self.assertEqual(self.stack.state, (self.stack.CREATE,
|
||||
self.stack.COMPLETE))
|
||||
return self.stack
|
||||
|
||||
@utils.stack_delete_after
|
||||
def test_template_resource_outputs(self):
|
||||
# assertion: if a template resource is given by file: we can later
|
||||
# do a stack-show and get the outputs. This tests that
|
||||
# we can retrieve the nested stack from the db.
|
||||
|
||||
stack = self.create_file_based_template_resource()
|
||||
templ_resource = stack['the_nested']
|
||||
self.assertEqual('myname', templ_resource.FnGetAtt('Foo'))
|
||||
|
||||
# this is the real test here: does a newly (re)loaded stack get it's
|
||||
# attributes (this happens on stack-show not create/update).
|
||||
status_stack = parser.Stack.load(self.ctx, stack_id=stack.id)
|
||||
templ_resource = status_stack['the_nested']
|
||||
self.assertEqual('myname', templ_resource.FnGetAtt('Foo'))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def create_stack(self, template):
|
||||
t = template_format.parse(template)
|
||||
stack = self.parse_stack(t)
|
||||
|
|
Loading…
Reference in New Issue