8c96a4d856
Six is in use to help us to keep support for python 2.7. Since the ussuri cycle we decide to remove the python 2.7 support so we can go ahead and also remove six usage from the python code. Review process and help ----------------------- Removing six introduce a lot of changes and an huge amount of modified files To simplify reviews we decided to split changes into several patches to avoid painful reviews and avoid mistakes. To review this patch you can use the six documentation [1] to obtain help and understand choices. Additional informations ----------------------- Changes related to 'six.b(data)' [2] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ six.b [2] encode the given datas in latin-1 in python3 so I did the same things in this patch. Latin-1 is equal to iso-8859-1 [3]. This encoding is the default encoding [4] of certain descriptive HTTP headers. I suggest to keep latin-1 for the moment and to move to another encoding in a follow-up patch if needed to move to most powerful encoding (utf8). HTML4 support utf8 charset and utf8 is the default charset for HTML5 [5]. Note that this commit message is autogenerated and not necesserly contains changes related to 'six.b' [1] https://six.readthedocs.io/ [2] https://six.readthedocs.io/#six.b [3] https://docs.python.org/3/library/codecs.html#standard-encodings [4] https://www.w3schools.com/charsets/ref_html_8859.asp [5] https://www.w3schools.com/html/html_charset.asp Patch 28 of a serie of 28 patches six fully removed now! Thank you six for the rendered services! Change-Id: If44ee4b565cc9390fa0422fba4dda080b4f90b98
984 lines
26 KiB
Python
984 lines
26 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import json
|
|
|
|
from heatclient import exc as heat_exceptions
|
|
import yaml
|
|
|
|
from heat_integrationtests.common import test
|
|
from heat_integrationtests.functional import functional_base
|
|
|
|
|
|
class TemplateResourceTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can use the registry in a nested provider."""
|
|
|
|
template = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
secret1:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
secret-out:
|
|
value: { get_attr: [secret1, value] }
|
|
'''
|
|
nested_templ = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
secret2:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
value:
|
|
value: { get_attr: [secret2, value] }
|
|
'''
|
|
|
|
env_templ = '''
|
|
resource_registry:
|
|
"OS::Heat::RandomString": nested.yaml
|
|
'''
|
|
|
|
def test_nested_env(self):
|
|
main_templ = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
secret1:
|
|
type: My::NestedSecret
|
|
outputs:
|
|
secret-out:
|
|
value: { get_attr: [secret1, value] }
|
|
'''
|
|
|
|
nested_templ = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
secret2:
|
|
type: My::Secret
|
|
outputs:
|
|
value:
|
|
value: { get_attr: [secret2, value] }
|
|
'''
|
|
|
|
env_templ = '''
|
|
resource_registry:
|
|
"My::Secret": "OS::Heat::RandomString"
|
|
"My::NestedSecret": nested.yaml
|
|
'''
|
|
|
|
stack_identifier = self.stack_create(
|
|
template=main_templ,
|
|
files={'nested.yaml': nested_templ},
|
|
environment=env_templ)
|
|
nested_ident = self.assert_resource_is_a_stack(stack_identifier,
|
|
'secret1')
|
|
# prove that resource.parent_resource is populated.
|
|
sec2 = self.client.resources.get(nested_ident, 'secret2')
|
|
self.assertEqual('secret1', sec2.parent_resource)
|
|
|
|
def test_no_infinite_recursion(self):
|
|
"""Prove that we can override a python resource.
|
|
|
|
And use that resource within the template resource.
|
|
"""
|
|
stack_identifier = self.stack_create(
|
|
template=self.template,
|
|
files={'nested.yaml': self.nested_templ},
|
|
environment=self.env_templ)
|
|
self.assert_resource_is_a_stack(stack_identifier, 'secret1')
|
|
|
|
def test_nested_stack_delete_then_delete_parent_stack(self):
|
|
"""Check the robustness of stack deletion.
|
|
|
|
This tests that if you manually delete a nested
|
|
stack, the parent stack is still deletable.
|
|
"""
|
|
# disable cleanup so we can call _stack_delete() directly.
|
|
stack_identifier = self.stack_create(
|
|
template=self.template,
|
|
files={'nested.yaml': self.nested_templ},
|
|
environment=self.env_templ,
|
|
enable_cleanup=False)
|
|
|
|
nested_ident = self.assert_resource_is_a_stack(stack_identifier,
|
|
'secret1')
|
|
|
|
self._stack_delete(nested_ident)
|
|
self._stack_delete(stack_identifier)
|
|
|
|
def test_change_in_file_path(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.template,
|
|
files={'nested.yaml': self.nested_templ},
|
|
environment=self.env_templ)
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
secret_out1 = self._stack_output(stack, 'secret-out')
|
|
|
|
nested_templ_2 = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
secret2:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
value:
|
|
value: freddy
|
|
'''
|
|
env_templ_2 = '''
|
|
resource_registry:
|
|
"OS::Heat::RandomString": new/nested.yaml
|
|
'''
|
|
self.update_stack(stack_identifier,
|
|
template=self.template,
|
|
files={'new/nested.yaml': nested_templ_2},
|
|
environment=env_templ_2)
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
secret_out2 = self._stack_output(stack, 'secret-out')
|
|
self.assertNotEqual(secret_out1, secret_out2)
|
|
self.assertEqual('freddy', secret_out2)
|
|
|
|
|
|
class NestedAttributesTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can use the template resource references."""
|
|
|
|
main_templ = '''
|
|
heat_template_version: 2014-10-16
|
|
resources:
|
|
secret2:
|
|
type: My::NestedSecret
|
|
outputs:
|
|
old_way:
|
|
value: { get_attr: [secret2, nested_str]}
|
|
test_attr1:
|
|
value: { get_attr: [secret2, resource.secret1, value]}
|
|
test_attr2:
|
|
value: { get_attr: [secret2, resource.secret1.value]}
|
|
test_ref:
|
|
value: { get_resource: secret2 }
|
|
'''
|
|
|
|
env_templ = '''
|
|
resource_registry:
|
|
"My::NestedSecret": nested.yaml
|
|
'''
|
|
|
|
def test_stack_ref(self):
|
|
nested_templ = '''
|
|
heat_template_version: 2014-10-16
|
|
resources:
|
|
secret1:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
nested_str:
|
|
value: {get_attr: [secret1, value]}
|
|
'''
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_templ,
|
|
files={'nested.yaml': nested_templ},
|
|
environment=self.env_templ)
|
|
self.assert_resource_is_a_stack(stack_identifier, 'secret2')
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
test_ref = self._stack_output(stack, 'test_ref')
|
|
self.assertIn('arn:openstack:heat:', test_ref)
|
|
|
|
def test_transparent_ref(self):
|
|
"""Test using nested resource more transparently.
|
|
|
|
With the addition of OS::stack_id we can now use the nested resource
|
|
more transparently.
|
|
"""
|
|
|
|
nested_templ = '''
|
|
heat_template_version: 2014-10-16
|
|
resources:
|
|
secret1:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
OS::stack_id:
|
|
value: {get_resource: secret1}
|
|
nested_str:
|
|
value: {get_attr: [secret1, value]}
|
|
'''
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_templ,
|
|
files={'nested.yaml': nested_templ},
|
|
environment=self.env_templ)
|
|
self.assert_resource_is_a_stack(stack_identifier, 'secret2')
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
test_ref = self._stack_output(stack, 'test_ref')
|
|
test_attr = self._stack_output(stack, 'old_way')
|
|
|
|
self.assertNotIn('arn:openstack:heat', test_ref)
|
|
self.assertEqual(test_attr, test_ref)
|
|
|
|
def test_nested_attributes(self):
|
|
nested_templ = '''
|
|
heat_template_version: 2014-10-16
|
|
resources:
|
|
secret1:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
nested_str:
|
|
value: {get_attr: [secret1, value]}
|
|
'''
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_templ,
|
|
files={'nested.yaml': nested_templ},
|
|
environment=self.env_templ)
|
|
self.assert_resource_is_a_stack(stack_identifier, 'secret2')
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
old_way = self._stack_output(stack, 'old_way')
|
|
test_attr1 = self._stack_output(stack, 'test_attr1')
|
|
test_attr2 = self._stack_output(stack, 'test_attr2')
|
|
|
|
self.assertEqual(old_way, test_attr1)
|
|
self.assertEqual(old_way, test_attr2)
|
|
|
|
|
|
class TemplateResourceFacadeTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can use ResourceFacade in a HOT template."""
|
|
|
|
main_template = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
the_nested:
|
|
type: the.yaml
|
|
metadata:
|
|
foo: bar
|
|
outputs:
|
|
value:
|
|
value: {get_attr: [the_nested, output]}
|
|
'''
|
|
|
|
nested_templ = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
test:
|
|
type: OS::Heat::TestResource
|
|
properties:
|
|
value: {"Fn::Select": [foo, {resource_facade: metadata}]}
|
|
outputs:
|
|
output:
|
|
value: {get_attr: [test, output]}
|
|
'''
|
|
|
|
def test_metadata(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
files={'the.yaml': self.nested_templ})
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
value = self._stack_output(stack, 'value')
|
|
self.assertEqual('bar', value)
|
|
|
|
|
|
class TemplateResourceUpdateTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can do template resource updates."""
|
|
|
|
main_template = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
the_nested:
|
|
Type: the.yaml
|
|
Properties:
|
|
one: my_name
|
|
two: your_name
|
|
Outputs:
|
|
identifier:
|
|
Value: {Ref: the_nested}
|
|
value:
|
|
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
|
'''
|
|
|
|
main_template_change_prop = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
the_nested:
|
|
Type: the.yaml
|
|
Properties:
|
|
one: updated_name
|
|
two: your_name
|
|
|
|
Outputs:
|
|
identifier:
|
|
Value: {Ref: the_nested}
|
|
value:
|
|
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
|
'''
|
|
|
|
main_template_add_prop = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
the_nested:
|
|
Type: the.yaml
|
|
Properties:
|
|
one: my_name
|
|
two: your_name
|
|
three: third_name
|
|
|
|
Outputs:
|
|
identifier:
|
|
Value: {Ref: the_nested}
|
|
value:
|
|
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
|
'''
|
|
|
|
main_template_remove_prop = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
the_nested:
|
|
Type: the.yaml
|
|
Properties:
|
|
one: my_name
|
|
|
|
Outputs:
|
|
identifier:
|
|
Value: {Ref: the_nested}
|
|
value:
|
|
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
|
'''
|
|
|
|
initial_tmpl = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: foo
|
|
Type: String
|
|
two:
|
|
Default: bar
|
|
Type: String
|
|
|
|
Resources:
|
|
NestedResource:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: one}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [NestedResource, value]}
|
|
'''
|
|
|
|
prop_change_tmpl = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: yikes
|
|
Type: String
|
|
two:
|
|
Default: foo
|
|
Type: String
|
|
Resources:
|
|
NestedResource:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: two}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [NestedResource, value]}
|
|
'''
|
|
|
|
prop_add_tmpl = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: yikes
|
|
Type: String
|
|
two:
|
|
Default: foo
|
|
Type: String
|
|
three:
|
|
Default: bar
|
|
Type: String
|
|
|
|
Resources:
|
|
NestedResource:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: three}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [NestedResource, value]}
|
|
'''
|
|
|
|
prop_remove_tmpl = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: yikes
|
|
Type: String
|
|
|
|
Resources:
|
|
NestedResource:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: one}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [NestedResource, value]}
|
|
'''
|
|
|
|
attr_change_tmpl = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: foo
|
|
Type: String
|
|
two:
|
|
Default: bar
|
|
Type: String
|
|
|
|
Resources:
|
|
NestedResource:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: one}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [NestedResource, value]}
|
|
something_else:
|
|
Value: just_a_string
|
|
'''
|
|
|
|
content_change_tmpl = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: foo
|
|
Type: String
|
|
two:
|
|
Default: bar
|
|
Type: String
|
|
|
|
Resources:
|
|
NestedResource:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: yum
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [NestedResource, value]}
|
|
'''
|
|
|
|
EXPECTED = (UPDATE, NOCHANGE) = ('update', 'nochange')
|
|
scenarios = [
|
|
('no_changes', dict(template=main_template,
|
|
provider=initial_tmpl,
|
|
expect=NOCHANGE)),
|
|
('main_tmpl_change', dict(template=main_template_change_prop,
|
|
provider=initial_tmpl,
|
|
expect=UPDATE)),
|
|
('provider_change', dict(template=main_template,
|
|
provider=content_change_tmpl,
|
|
expect=UPDATE)),
|
|
('provider_props_change', dict(template=main_template,
|
|
provider=prop_change_tmpl,
|
|
expect=UPDATE)),
|
|
('provider_props_add', dict(template=main_template_add_prop,
|
|
provider=prop_add_tmpl,
|
|
expect=UPDATE)),
|
|
('provider_props_remove', dict(template=main_template_remove_prop,
|
|
provider=prop_remove_tmpl,
|
|
expect=NOCHANGE)),
|
|
('provider_attr_change', dict(template=main_template,
|
|
provider=attr_change_tmpl,
|
|
expect=NOCHANGE)),
|
|
]
|
|
|
|
def test_template_resource_update_template_schema(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
files={'the.yaml': self.initial_tmpl})
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
initial_id = self._stack_output(stack, 'identifier')
|
|
initial_val = self._stack_output(stack, 'value')
|
|
|
|
self.update_stack(stack_identifier,
|
|
self.template,
|
|
files={'the.yaml': self.provider})
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
self.assertEqual(initial_id,
|
|
self._stack_output(stack, 'identifier'))
|
|
if self.expect == self.NOCHANGE:
|
|
self.assertEqual(initial_val,
|
|
self._stack_output(stack, 'value'))
|
|
else:
|
|
self.assertNotEqual(initial_val,
|
|
self._stack_output(stack, 'value'))
|
|
|
|
|
|
class TemplateResourceUpdateFailedTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can do updates on a nested stack to fix a stack."""
|
|
|
|
main_template = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
keypair:
|
|
Type: OS::Nova::KeyPair
|
|
Properties:
|
|
name: replace-this
|
|
save_private_key: false
|
|
server:
|
|
Type: server_fail.yaml
|
|
DependsOn: keypair
|
|
'''
|
|
nested_templ = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
RealRandom:
|
|
Type: OS::Heat::RandomString
|
|
'''
|
|
|
|
def setUp(self):
|
|
super(TemplateResourceUpdateFailedTest, self).setUp()
|
|
self.assign_keypair()
|
|
|
|
def test_update_on_failed_create(self):
|
|
# create a stack with "server" dependent on "keypair", but
|
|
# keypair fails, so "server" is not created properly.
|
|
# We then fix the template and it should succeed.
|
|
broken_templ = self.main_template.replace('replace-this',
|
|
self.keypair_name)
|
|
stack_identifier = self.stack_create(
|
|
template=broken_templ,
|
|
files={'server_fail.yaml': self.nested_templ},
|
|
expected_status='CREATE_FAILED')
|
|
|
|
fixed_templ = self.main_template.replace('replace-this',
|
|
test.rand_name())
|
|
self.update_stack(stack_identifier,
|
|
fixed_templ,
|
|
files={'server_fail.yaml': self.nested_templ})
|
|
|
|
|
|
class TemplateResourceAdoptTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can do template resource adopt/abandon."""
|
|
|
|
main_template = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
the_nested:
|
|
Type: the.yaml
|
|
Properties:
|
|
one: my_name
|
|
Outputs:
|
|
identifier:
|
|
Value: {Ref: the_nested}
|
|
value:
|
|
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
|
'''
|
|
|
|
nested_templ = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: foo
|
|
Type: String
|
|
Resources:
|
|
RealRandom:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: one}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [RealRandom, value]}
|
|
'''
|
|
|
|
def _yaml_to_json(self, yaml_templ):
|
|
return yaml.safe_load(yaml_templ)
|
|
|
|
def test_abandon(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
files={'the.yaml': self.nested_templ},
|
|
enable_cleanup=False
|
|
)
|
|
|
|
info = self.stack_abandon(stack_id=stack_identifier)
|
|
self.assertEqual(self._yaml_to_json(self.main_template),
|
|
info['template'])
|
|
self.assertEqual(self._yaml_to_json(self.nested_templ),
|
|
info['resources']['the_nested']['template'])
|
|
# TODO(james combs): Implement separate test cases for export
|
|
# once export REST API is available. Also test reverse order
|
|
# of invocation: export -> abandon AND abandon -> export
|
|
|
|
def test_adopt(self):
|
|
data = {
|
|
'resources': {
|
|
'the_nested': {
|
|
"type": "the.yaml",
|
|
"resources": {
|
|
"RealRandom": {
|
|
"type": "OS::Heat::RandomString",
|
|
'resource_data': {'value': 'goopie'},
|
|
'resource_id': 'froggy'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"environment": {"parameters": {}},
|
|
"template": yaml.safe_load(self.main_template)
|
|
}
|
|
|
|
stack_identifier = self.stack_adopt(
|
|
adopt_data=json.dumps(data),
|
|
files={'the.yaml': self.nested_templ})
|
|
|
|
self.assert_resource_is_a_stack(stack_identifier, 'the_nested')
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
self.assertEqual('goopie', self._stack_output(stack, 'value'))
|
|
|
|
|
|
class TemplateResourceCheckTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can do template resource check."""
|
|
|
|
main_template = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
the_nested:
|
|
Type: the.yaml
|
|
Properties:
|
|
one: my_name
|
|
Outputs:
|
|
identifier:
|
|
Value: {Ref: the_nested}
|
|
value:
|
|
Value: {'Fn::GetAtt': [the_nested, the_str]}
|
|
'''
|
|
|
|
nested_templ = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Parameters:
|
|
one:
|
|
Default: foo
|
|
Type: String
|
|
Resources:
|
|
RealRandom:
|
|
Type: OS::Heat::RandomString
|
|
Properties:
|
|
salt: {Ref: one}
|
|
Outputs:
|
|
the_str:
|
|
Value: {'Fn::GetAtt': [RealRandom, value]}
|
|
'''
|
|
|
|
def test_check(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
files={'the.yaml': self.nested_templ}
|
|
)
|
|
|
|
self.client.actions.check(stack_id=stack_identifier)
|
|
self._wait_for_stack_status(stack_identifier, 'CHECK_COMPLETE')
|
|
|
|
|
|
class TemplateResourceErrorMessageTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that nested stack errors don't suck."""
|
|
|
|
template = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
victim:
|
|
Type: fail.yaml
|
|
'''
|
|
nested_templ = '''
|
|
HeatTemplateFormatVersion: '2012-12-12'
|
|
Resources:
|
|
oops:
|
|
Type: OS::Heat::TestResource
|
|
Properties:
|
|
fail: true
|
|
wait_secs: 2
|
|
'''
|
|
|
|
def test_fail(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.template,
|
|
files={'fail.yaml': self.nested_templ},
|
|
expected_status='CREATE_FAILED')
|
|
stack = self.client.stacks.get(stack_identifier)
|
|
|
|
exp_path = 'resources.victim.resources.oops'
|
|
exp_msg = 'Test Resource failed oops'
|
|
exp = 'Resource CREATE failed: ValueError: %s: %s' % (exp_path,
|
|
exp_msg)
|
|
self.assertEqual(exp, stack.stack_status_reason)
|
|
|
|
|
|
class TemplateResourceSuspendResumeTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that we can do template resource suspend/resume."""
|
|
|
|
main_template = '''
|
|
heat_template_version: 2014-10-16
|
|
parameters:
|
|
resources:
|
|
the_nested:
|
|
type: the.yaml
|
|
'''
|
|
|
|
nested_templ = '''
|
|
heat_template_version: 2014-10-16
|
|
resources:
|
|
test_random_string:
|
|
type: OS::Heat::RandomString
|
|
'''
|
|
|
|
def test_suspend_resume(self):
|
|
"""Basic test for template resource suspend resume."""
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
files={'the.yaml': self.nested_templ}
|
|
)
|
|
|
|
self.stack_suspend(stack_identifier=stack_identifier)
|
|
self.stack_resume(stack_identifier=stack_identifier)
|
|
|
|
|
|
class ValidateFacadeTest(functional_base.FunctionalTestsBase):
|
|
"""Prove that nested stack errors don't suck."""
|
|
|
|
template = '''
|
|
heat_template_version: 2015-10-15
|
|
resources:
|
|
thisone:
|
|
type: OS::Thingy
|
|
properties:
|
|
one: pre
|
|
two: post
|
|
outputs:
|
|
one:
|
|
value: {get_attr: [thisone, here-it-is]}
|
|
'''
|
|
templ_facade = '''
|
|
heat_template_version: 2015-04-30
|
|
parameters:
|
|
one:
|
|
type: string
|
|
two:
|
|
type: string
|
|
outputs:
|
|
here-it-is:
|
|
value: noop
|
|
'''
|
|
env = '''
|
|
resource_registry:
|
|
OS::Thingy: facade.yaml
|
|
resources:
|
|
thisone:
|
|
OS::Thingy: concrete.yaml
|
|
'''
|
|
|
|
def setUp(self):
|
|
super(ValidateFacadeTest, self).setUp()
|
|
self.client = self.orchestration_client
|
|
|
|
def test_missing_param(self):
|
|
templ_missing_parameter = '''
|
|
heat_template_version: 2015-04-30
|
|
parameters:
|
|
one:
|
|
type: string
|
|
resources:
|
|
str:
|
|
type: OS::Heat::RandomString
|
|
outputs:
|
|
here-it-is:
|
|
value:
|
|
not-important
|
|
'''
|
|
template = yaml.safe_load(self.template)
|
|
del template['resources']['thisone']['properties']['two']
|
|
try:
|
|
self.stack_create(
|
|
template=yaml.safe_dump(template),
|
|
environment=self.env,
|
|
files={'facade.yaml': self.templ_facade,
|
|
'concrete.yaml': templ_missing_parameter},
|
|
expected_status='CREATE_FAILED')
|
|
except heat_exceptions.HTTPBadRequest as exc:
|
|
exp = ('ERROR: Required property two for facade '
|
|
'OS::Thingy missing in provider')
|
|
self.assertEqual(exp, str(exc))
|
|
|
|
def test_missing_output(self):
|
|
templ_missing_output = '''
|
|
heat_template_version: 2015-04-30
|
|
parameters:
|
|
one:
|
|
type: string
|
|
two:
|
|
type: string
|
|
resources:
|
|
str:
|
|
type: OS::Heat::RandomString
|
|
'''
|
|
try:
|
|
self.stack_create(
|
|
template=self.template,
|
|
environment=self.env,
|
|
files={'facade.yaml': self.templ_facade,
|
|
'concrete.yaml': templ_missing_output},
|
|
expected_status='CREATE_FAILED')
|
|
except heat_exceptions.HTTPBadRequest as exc:
|
|
exp = ('ERROR: Attribute here-it-is for facade '
|
|
'OS::Thingy missing in provider')
|
|
self.assertEqual(exp, str(exc))
|
|
|
|
|
|
class TemplateResourceNewParamTest(functional_base.FunctionalTestsBase):
|
|
|
|
main_template = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
my_resource:
|
|
type: resource.yaml
|
|
properties:
|
|
value1: foo
|
|
'''
|
|
nested_templ = '''
|
|
heat_template_version: 2013-05-23
|
|
parameters:
|
|
value1:
|
|
type: string
|
|
resources:
|
|
test:
|
|
type: OS::Heat::TestResource
|
|
properties:
|
|
value: {get_param: value1}
|
|
'''
|
|
main_template_update = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
my_resource:
|
|
type: resource.yaml
|
|
properties:
|
|
value1: foo
|
|
value2: foo
|
|
'''
|
|
nested_templ_update_fail = '''
|
|
heat_template_version: 2013-05-23
|
|
parameters:
|
|
value1:
|
|
type: string
|
|
value2:
|
|
type: string
|
|
resources:
|
|
test:
|
|
type: OS::Heat::TestResource
|
|
properties:
|
|
fail: True
|
|
value:
|
|
str_replace:
|
|
template: VAL1-VAL2
|
|
params:
|
|
VAL1: {get_param: value1}
|
|
VAL2: {get_param: value2}
|
|
'''
|
|
nested_templ_update = '''
|
|
heat_template_version: 2013-05-23
|
|
parameters:
|
|
value1:
|
|
type: string
|
|
value2:
|
|
type: string
|
|
resources:
|
|
test:
|
|
type: OS::Heat::TestResource
|
|
properties:
|
|
value:
|
|
str_replace:
|
|
template: VAL1-VAL2
|
|
params:
|
|
VAL1: {get_param: value1}
|
|
VAL2: {get_param: value2}
|
|
'''
|
|
|
|
def test_update(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
files={'resource.yaml': self.nested_templ})
|
|
|
|
# Make the update fails with the new parameter inserted.
|
|
self.update_stack(
|
|
stack_identifier,
|
|
self.main_template_update,
|
|
files={'resource.yaml': self.nested_templ_update_fail},
|
|
expected_status='UPDATE_FAILED')
|
|
|
|
# Fix the update, it should succeed now.
|
|
self.update_stack(
|
|
stack_identifier,
|
|
self.main_template_update,
|
|
files={'resource.yaml': self.nested_templ_update})
|
|
|
|
|
|
class TemplateResourceRemovedParamTest(functional_base.FunctionalTestsBase):
|
|
|
|
main_template = '''
|
|
heat_template_version: 2013-05-23
|
|
parameters:
|
|
value1:
|
|
type: string
|
|
default: foo
|
|
resources:
|
|
my_resource:
|
|
type: resource.yaml
|
|
properties:
|
|
value1: {get_param: value1}
|
|
'''
|
|
nested_templ = '''
|
|
heat_template_version: 2013-05-23
|
|
parameters:
|
|
value1:
|
|
type: string
|
|
default: foo
|
|
resources:
|
|
test:
|
|
type: OS::Heat::TestResource
|
|
properties:
|
|
value: {get_param: value1}
|
|
'''
|
|
main_template_update = '''
|
|
heat_template_version: 2013-05-23
|
|
resources:
|
|
my_resource:
|
|
type: resource.yaml
|
|
'''
|
|
nested_templ_update = '''
|
|
heat_template_version: 2013-05-23
|
|
parameters:
|
|
value1:
|
|
type: string
|
|
default: foo
|
|
value2:
|
|
type: string
|
|
default: bar
|
|
resources:
|
|
test:
|
|
type: OS::Heat::TestResource
|
|
properties:
|
|
value:
|
|
str_replace:
|
|
template: VAL1-VAL2
|
|
params:
|
|
VAL1: {get_param: value1}
|
|
VAL2: {get_param: value2}
|
|
'''
|
|
|
|
def test_update(self):
|
|
stack_identifier = self.stack_create(
|
|
template=self.main_template,
|
|
environment={'parameters': {'value1': 'spam'}},
|
|
files={'resource.yaml': self.nested_templ})
|
|
|
|
self.update_stack(
|
|
stack_identifier,
|
|
self.main_template_update,
|
|
environment={'parameter_defaults': {'value2': 'egg'}},
|
|
files={'resource.yaml': self.nested_templ_update}, existing=True)
|