def test_incorrect_template_provided_with_url(self): wrong_template = ''' <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# ''' env = environment.Environment() test_templ_name = 'http://heatr/bad_tmpl.yaml' env.load({'resource_registry': {'Test::Tmpl': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template(empty_template, env=env), stack_id=str(uuid.uuid4())) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https') ).AndReturn(wrong_template) self.m.ReplayAll() definition = rsrc_defn.ResourceDefinition('test_t_res', 'Test::Tmpl') temp_res = template_resource.TemplateResource('test_t_res', definition, stack) err = self.assertRaises(exception.StackValidationFailed, temp_res.validate) self.assertIn('Error parsing template: ', six.text_type(err)) self.m.VerifyAll()
def test_user_template_retrieve_fail(self): # make sure that a TemplateResource defined in the user environment # fails gracefully if the template file specified is inaccessible # we should be able to create the TemplateResource object, but # validation should fail, when the second attempt to access it is # made in validate() env = environment.Environment() test_templ_name = 'http://heatr/noexist.yaml' env.load({'resource_registry': {'Test::Flippy': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template(empty_template), env=env, stack_id=str(uuid.uuid4())) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https'))\ .AndRaise(urlfetch.URLFetchError(_('Failed to retrieve template'))) self.m.ReplayAll() definition = rsrc_defn.ResourceDefinition('test_t_res', 'Test::Flippy') temp_res = template_resource.TemplateResource('test_t_res', definition, stack) self.assertRaises(exception.StackValidationFailed, temp_res.validate) self.m.VerifyAll()
def test_nested_stack_adopt_fail(self): resource._register_class('GenericResource', generic_rsrc.GenericResource) urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(''' HeatTemplateFormatVersion: '2012-12-12' Parameters: KeyName: Type: String Resources: NestedResource: Type: GenericResource Outputs: Foo: Value: bar ''') self.m.ReplayAll() adopt_data = { "resources": { "the_nested": { "resource_id": "test-res-id", "resources": { } } } } stack = self.adopt_stack(self.test_template, adopt_data) rsrc = stack['the_nested'] self.assertEqual((rsrc.ADOPT, rsrc.FAILED), rsrc.nested().state) nested_name = utils.PhysName(stack.name, 'the_nested') self.assertEqual(nested_name, rsrc.physical_resource_name()) rsrc.delete() self.m.VerifyAll()
def test_nested_stack_create(self): urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack['the_nested'] nested_name = utils.PhysName(stack.name, 'the_nested') self.assertEqual(rsrc.physical_resource_name(), nested_name) arn_prefix = ('arn:openstack:heat::aaaa:stacks/%s/' % rsrc.physical_resource_name()) self.assertTrue(rsrc.FnGetRefId().startswith(arn_prefix)) self.assertEqual('bar', rsrc.FnGetAtt('Outputs.Foo')) self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Foo') self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Outputs.Bar') self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Bar') rsrc.delete() self.assertTrue(rsrc.FnGetRefId().startswith(arn_prefix)) self.m.VerifyAll()
def test_system_template_retrieve_by_file(self): # make sure that a TemplateResource defined in the global environment # can be created and the template retrieved using the "file:" # scheme. g_env = resources.global_env() test_templ_name = 'file:///etc/heatr/frodo.yaml' g_env.load({'resource_registry': {'Test::Frodo': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template(empty_template), stack_id=str(uuid.uuid4())) minimal_temp = json.dumps({'HeatTemplateFormatVersion': '2012-12-12', 'Parameters': {}, 'Resources': {}}) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https', 'file')).AndReturn(minimal_temp) self.m.ReplayAll() definition = rsrc_defn.ResourceDefinition('test_t_res', 'Test::Frodo') temp_res = template_resource.TemplateResource('test_t_res', definition, stack) self.assertIsNone(temp_res.validate()) self.m.VerifyAll()
def test_nested_stack_create(self): urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack['the_nested'] nested_name = utils.PhysName(stack.name, 'the_nested') self.assertEqual(nested_name, rsrc.physical_resource_name()) arn_prefix = ('arn:openstack:heat::aaaa:stacks/%s/' % rsrc.physical_resource_name()) self.assertTrue(rsrc.FnGetRefId().startswith(arn_prefix)) self.assertEqual('bar', rsrc.FnGetAtt('Outputs.Foo')) self.assertRaises( exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Foo') self.assertRaises( exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Outputs.Bar') self.assertRaises( exception.InvalidTemplateAttribute, rsrc.FnGetAtt, 'Bar') rsrc.delete() self.assertTrue(rsrc.FnGetRefId().startswith(arn_prefix)) self.m.VerifyAll()
def test_template_as_resource(self): """ Test that the resulting resource has the right prop and attrib schema. Note that this test requires the Wordpress_Single_Instance.yaml template in the templates directory since we want to test using a non-trivial template. """ test_templ_name = "WordPress_Single_Instance.yaml" path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates', test_templ_name) # check if its in the directory list vs. exists to work around # case-insensitive file systems self.assertIn(test_templ_name, os.listdir(os.path.dirname(path))) with open(path) as test_templ_file: test_templ = test_templ_file.read() self.assertTrue(test_templ, "Empty test template") self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('file',))\ .AndRaise(urlfetch.URLFetchError(_('Failed to retrieve template'))) urlfetch.get(test_templ_name, allowed_schemes=('http', 'https')).AndReturn(test_templ) parsed_test_templ = template_format.parse(test_templ) self.m.ReplayAll() json_snippet = { "Type": test_templ_name, "Properties": { "KeyName": "mykeyname", "DBName": "wordpress1", "DBUsername": "******", "DBPassword": "******", "DBRootPassword": "******", "LinuxDistribution": "U10" } } stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template(empty_template), stack_id=str(uuid.uuid4())) templ_resource = resource.Resource("test_templ_resource", json_snippet, stack) self.m.VerifyAll() self.assertIsInstance(templ_resource, template_resource.TemplateResource) for prop in parsed_test_templ.get("Parameters", {}): self.assertIn(prop, templ_resource.properties) for attrib in parsed_test_templ.get("Outputs", {}): self.assertIn(attrib, templ_resource.attributes) for k, v in json_snippet.get("Properties").items(): self.assertEqual(v, templ_resource.properties[k]) self.assertEqual( { 'WordPress_Single_Instance.yaml': 'WordPress_Single_Instance.yaml', 'resources': {} }, stack.env.user_env_as_dict()["resource_registry"]) self.assertNotIn('WordPress_Single_Instance.yaml', resources.global_env().registry._registry)
def test_nested_stack_update(self): urlfetch.get("https://localhost/the.template").MultipleTimes().AndReturn(self.nested_template) urlfetch.get("https://localhost/new.template").MultipleTimes().AndReturn(self.update_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack["the_nested"] original_nested_id = rsrc.resource_id t = template_format.parse(self.test_template) new_res = copy.deepcopy(t["Resources"]["the_nested"]) new_res["Properties"]["TemplateURL"] = "https://localhost/new.template" prop_diff = {"TemplateURL": "https://localhost/new.template"} rsrc.handle_update(new_res, {}, prop_diff) # Expect the physical resource name staying the same after update, # so that the nested was actually updated instead of replaced. self.assertEqual(original_nested_id, rsrc.resource_id) db_nested = db_api.stack_get(stack.context, rsrc.resource_id) # Owner_id should be preserved during the update process. self.assertEqual(stack.id, db_nested.owner_id) self.assertEqual("foo", rsrc.FnGetAtt("Outputs.Bar")) self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, "Foo") self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, "Outputs.Foo") self.assertRaises(exception.InvalidTemplateAttribute, rsrc.FnGetAtt, "Bar") rsrc.delete() self.m.VerifyAll()
def test_system_template_retrieve_fail(self): # make sure that a TemplateResource defined in the global environment # fails gracefully if the template file specified is inaccessible # we should be able to create the TemplateResource object, but # validation should fail, when the second attempt to access it is # made in validate() g_env = resources.global_env() test_templ_name = 'file:///etc/heatr/frodo.yaml' g_env.load({'resource_registry': {'Test::Frodo': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template(empty_template), stack_id=str(uuid.uuid4())) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https', 'file') ).AndRaise(urlfetch.URLFetchError( _('Failed to retrieve template'))) self.m.ReplayAll() definition = rsrc_defn.ResourceDefinition('test_t_res', 'Test::Frodo') temp_res = template_resource.TemplateResource('test_t_res', definition, stack) self.assertRaises(exception.StackValidationFailed, temp_res.validate) self.m.VerifyAll()
def test_user_template_retrieve_fail(self): # make sure that a TemplateResource defined in the user environment # fails gracefully if the template file specified is inaccessible # we should be able to create the TemplateResource object, but # validation should fail, when the second attempt to access it is # made in validate() env = environment.Environment() test_templ_name = 'http://heatr/noexist.yaml' env.load({'resource_registry': {'Test::Flippy': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template({}), env=env, stack_id=uuidutils.generate_uuid()) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https')).AndRaise(IOError) urlfetch.get(test_templ_name, allowed_schemes=('http', 'https')).AndRaise(IOError) self.m.ReplayAll() temp_res = template_resource.TemplateResource('test_t_res', {"Type": 'Test::Flippy'}, stack) self.assertRaises(exception.StackValidationFailed, temp_res.validate) self.m.VerifyAll()
def test_nested_stack_create_equals_resource_limit(self): cfg.CONF.set_override('max_resources_per_stack', 2) resource._register_class('GenericResource', generic_rsrc.GenericResource) urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(''' HeatTemplateFormatVersion: '2012-12-12' Parameters: KeyName: Type: String Resources: NestedResource: Type: GenericResource Outputs: Foo: Value: bar ''') self.m.ReplayAll() t = template_format.parse(self.test_template) stack = self.parse_stack(t) stack.create() self.assertEquals(stack.state, (stack.CREATE, stack.COMPLETE)) self.assertIn('NestedResource', stack['the_nested'].nested()) self.m.VerifyAll()
def test_nested_stack_update_exceeds_limit(self): resource._register_class("GenericResource", generic_rsrc.GenericResource) urlfetch.get("https://server.test/the.template").MultipleTimes().AndReturn(self.nested_template) urlfetch.get("https://server.test/new.template").MultipleTimes().AndReturn( """ HeatTemplateFormatVersion: '2012-12-12' Parameters: KeyName: Type: String Resources: NestedResource: Type: GenericResource Outputs: Bar: Value: foo """ ) self.m.ReplayAll() stack = self.create_stack(self.test_template) cfg.CONF.set_override("max_resources_per_stack", 1) rsrc = stack["the_nested"] t = template_format.parse(self.test_template) new_res = copy.deepcopy(t["Resources"]["the_nested"]) new_res["Properties"]["TemplateURL"] = "https://server.test/new.template" prop_diff = {"TemplateURL": "https://server.test/new.template"} ex = self.assertRaises(exception.RequestLimitExceeded, rsrc.handle_update, new_res, {}, prop_diff) self.assertIn(exception.StackResourceLimitExceeded.msg_fmt, str(ex)) rsrc.delete() self.m.VerifyAll()
def test_incorrect_template_provided_with_url(self): wrong_template = ''' <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# ''' env = environment.Environment() test_templ_name = 'http://heatr/bad_tmpl.yaml' env.load({'resource_registry': {'Test::Tmpl': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', template.Template(empty_template, env=env), stack_id=str(uuid.uuid4())) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https') ).AndReturn(wrong_template) self.m.ReplayAll() definition = rsrc_defn.ResourceDefinition('test_t_res', 'Test::Tmpl') temp_res = template_resource.TemplateResource('test_t_res', definition, stack) err = self.assertRaises(exception.StackValidationFailed, temp_res.validate) self.assertIn('Error parsing template: ', six.text_type(err)) self.m.VerifyAll()
def test_nested_stack_create_exceeds_resource_limit(self): cfg.CONF.set_override("max_resources_per_stack", 1) resource._register_class("GenericResource", generic_rsrc.GenericResource) urlfetch.get("https://server.test/the.template").MultipleTimes().AndReturn( """ HeatTemplateFormatVersion: '2012-12-12' Parameters: KeyName: Type: String Resources: NestedResource: Type: GenericResource Outputs: Foo: Value: bar """ ) self.m.ReplayAll() t = template_format.parse(self.test_template) stack = self.parse_stack(t) stack.create() self.assertEqual((stack.CREATE, stack.FAILED), stack.state) self.assertIn("Maximum resources per stack exceeded", stack.status_reason) self.m.VerifyAll()
def test_nested_stack_adopt_fail(self): resource._register_class('GenericResource', generic_rsrc.GenericResource) urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(''' HeatTemplateFormatVersion: '2012-12-12' Parameters: KeyName: Type: String Resources: NestedResource: Type: GenericResource Outputs: Foo: Value: bar ''') self.m.ReplayAll() adopt_data = { "resources": { "the_nested": { "resource_id": "test-res-id", "resources": {} } } } stack = self.adopt_stack(self.test_template, adopt_data) rsrc = stack['the_nested'] self.assertEqual((rsrc.ADOPT, rsrc.FAILED), rsrc.nested().state) nested_name = utils.PhysName(stack.name, 'the_nested') self.assertEqual(nested_name, rsrc.physical_resource_name()) rsrc.delete() self.m.VerifyAll()
def test_nested_stack_create_equals_resource_limit(self): cfg.CONF.set_override('max_resources_per_stack', 2) resource._register_class('GenericResource', generic_rsrc.GenericResource) urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(''' HeatTemplateFormatVersion: '2012-12-12' Parameters: KeyName: Type: String Resources: NestedResource: Type: GenericResource Outputs: Foo: Value: bar ''') self.m.ReplayAll() t = template_format.parse(self.test_template) stack = self.parse_stack(t) stack.create() self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state) self.assertIn('NestedResource', stack['the_nested'].nested()) self.m.VerifyAll()
def test_system_template_retrieve_fail(self): # make sure that a TemplateResource defined in the global environment # fails gracefully if the template file specified is inaccessible # we should be able to create the TemplateResource object, but # validation should fail, when the second attempt to access it is # made in validate() g_env = resources.global_env() test_templ_name = 'file:///etc/heatr/frodo.yaml' g_env.load({'resource_registry': {'Test::Frodo': test_templ_name}}) stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template({}), stack_id=uuidutils.generate_uuid()) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https', 'file')).AndRaise(IOError) urlfetch.get(test_templ_name, allowed_schemes=('http', 'https', 'file')).AndRaise(IOError) self.m.ReplayAll() temp_res = template_resource.TemplateResource('test_t_res', {"Type": 'Test::Frodo'}, stack) self.assertRaises(exception.StackValidationFailed, temp_res.validate) self.m.VerifyAll()
def test_get_template_resource_class(self): test_templ_name = 'file:///etc/heatr/frodo.yaml' minimal_temp = json.dumps({ 'HeatTemplateFormatVersion': '2012-12-12', 'Parameters': {}, 'Resources': {} }) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('file', )).AndReturn(minimal_temp) self.m.ReplayAll() env_str = { 'resource_registry': { 'resources': { 'fred': { "OS::ResourceType": test_templ_name } } } } global_env = environment.Environment({}, user_env=False) global_env.load(env_str) with mock.patch('heat.engine.resources._environment', global_env): env = environment.Environment({}) cls = env.get_class('OS::ResourceType', 'fred') self.assertNotEqual(template_resource.TemplateResource, cls) self.assertTrue(issubclass(cls, template_resource.TemplateResource)) self.assertTrue(hasattr(cls, "properties_schema")) self.assertTrue(hasattr(cls, "attributes_schema")) self.m.VerifyAll()
def test_template_as_resource(self): """ Test that the resulting resource has the right prop and attrib schema. Note that this test requires the Wordpress_Single_Instance.yaml template in the templates directory since we want to test using a non-trivial template. """ test_templ_name = "WordPress_Single_Instance.yaml" path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates', test_templ_name) # check if its in the directory list vs. exists to work around # case-insensitive file systems self.assertIn(test_templ_name, os.listdir(os.path.dirname(path))) with open(path) as test_templ_file: test_templ = test_templ_file.read() self.assertTrue(test_templ, "Empty test template") self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('file',) ).AndRaise(urlfetch.URLFetchError( _('Failed to retrieve template'))) urlfetch.get(test_templ_name, allowed_schemes=('http', 'https')).AndReturn(test_templ) parsed_test_templ = template_format.parse(test_templ) self.m.ReplayAll() stack = parser.Stack(utils.dummy_context(), 'test_stack', template.Template(empty_template), stack_id=str(uuid.uuid4())) properties = { "KeyName": "mykeyname", "DBName": "wordpress1", "DBUsername": "******", "DBPassword": "******", "DBRootPassword": "******", "LinuxDistribution": "U10" } definition = rsrc_defn.ResourceDefinition("test_templ_resource", test_templ_name, properties) templ_resource = resource.Resource("test_templ_resource", definition, stack) self.m.VerifyAll() self.assertIsInstance(templ_resource, template_resource.TemplateResource) for prop in parsed_test_templ.get("Parameters", {}): self.assertIn(prop, templ_resource.properties) for attrib in parsed_test_templ.get("Outputs", {}): self.assertIn(attrib, templ_resource.attributes) for k, v in properties.items(): self.assertEqual(v, templ_resource.properties[k]) self.assertEqual( {'WordPress_Single_Instance.yaml': 'WordPress_Single_Instance.yaml', 'resources': {}}, stack.env.user_env_as_dict()["resource_registry"]) self.assertNotIn('WordPress_Single_Instance.yaml', resources.global_env().registry._registry)
def test_res_data_delete(self): urlfetch.get("https://localhost/the.template").AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) res = stack["the_nested"].nested()["nested_res"] stack.delete() self.assertEqual(stack.state, (stack.DELETE, stack.COMPLETE)) self.assertRaises(exception.NotFound, db_api.resource_data_get, res, "test")
def test_max_fetch_size_okay(self): url = 'http://example.com/template' data = b'{ "foo": "bar" }' response = Response(data) cfg.CONF.set_override('max_template_size', 500) mock_get = self.patchobject(requests, 'get') mock_get.return_value = response urlfetch.get(url) mock_get.assert_called_once_with(url, stream=True)
def test_max_fetch_size_okay(self): url = 'http://example.com/template' data = '{ "foo": "bar" }' response = Response(data) cfg.CONF.set_override('max_template_size', 500) requests.get(url, stream=True).AndReturn(response) self.m.ReplayAll() urlfetch.get(url) self.m.VerifyAll()
def test_max_fetch_size_okay(self): url = 'http://example.com/template' data = b'{ "foo": "bar" }' response = Response(data) cfg.CONF.set_override('max_template_size', 500, enforce_type=True) requests.get(url, stream=True).AndReturn(response) self.m.ReplayAll() urlfetch.get(url) self.m.VerifyAll()
def test_res_data_delete(self): urlfetch.get('https://server.test/the.template').AndReturn( self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) res = stack['the_nested'].nested()['nested_res'] stack.delete() self.assertEqual(stack.state, (stack.DELETE, stack.COMPLETE)) self.assertRaises(exception.NotFound, db_api.resource_data_get, res, 'test')
def test_res_data_delete(self): urlfetch.get('https://server.test/the.template').AndReturn( self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) res = stack['the_nested'].nested()['nested_res'] stack.delete() self.assertEqual((stack.DELETE, stack.COMPLETE), stack.state) self.assertRaises(exception.NotFound, db_api.resource_data_get, res, 'test')
def test_nested_stack_create_with_timeout(self): urlfetch.get("https://server.test/the.template").MultipleTimes().AndReturn(self.nested_template) self.m.ReplayAll() timeout_template = template_format.parse(copy.deepcopy(self.test_template)) props = timeout_template["Resources"]["the_nested"]["Properties"] props["TimeoutInMinutes"] = "50" stack = self.create_stack(json.dumps(timeout_template)) self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state) self.m.VerifyAll()
def test_template_resource_update(self): # assertion: updating a template resource is never destructive # as it defers to the nested stack to determine if anything # needs to be replaced. utils.setup_dummy_db() resource._register_class('GenericResource', generic_rsrc.GenericResource) templ_resource_name = 'http://server.test/the.yaml' test_template = ''' HeatTemplateFormatVersion: '2012-12-12' Resources: the_nested: Type: %s Properties: one: myname ''' % templ_resource_name self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(templ_resource_name, allowed_schemes=('http', 'https')).MultipleTimes().\ AndReturn(''' HeatTemplateFormatVersion: '2012-12-12' Parameters: one: Type: String Resources: NestedResource: Type: GenericResource Outputs: Foo: Value: {Ref: one} ''') self.m.ReplayAll() stack = self.create_stack(test_template) templ_resource = stack['the_nested'] self.assertEqual('myname', templ_resource.FnGetAtt('Foo')) update_snippet = { "Type": templ_resource_name, "Properties": { "one": "yourname" } } # test that update() does NOT raise UpdateReplace. updater = scheduler.TaskRunner(templ_resource.update, update_snippet) self.assertEqual(None, updater()) self.assertEqual('yourname', templ_resource.FnGetAtt('Foo')) self.m.VerifyAll()
def test_nested_stack_create_with_timeout(self): urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(self.nested_template) self.m.ReplayAll() timeout_template = template_format.parse( copy.deepcopy(self.test_template)) props = timeout_template['Resources']['the_nested']['Properties'] props['TimeoutInMinutes'] = '50' stack = self.create_stack(json.dumps(timeout_template)) self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state) self.m.VerifyAll()
def test_nested_stack_delete(self): urlfetch.get("https://server.test/the.template").MultipleTimes().AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack["the_nested"] scheduler.TaskRunner(rsrc.delete)() self.assertEqual((stack.DELETE, stack.COMPLETE), rsrc.state) nested_stack = parser.Stack.load(utils.dummy_context("test_username", "aaaa", "password"), rsrc.resource_id) self.assertEqual((stack.DELETE, stack.COMPLETE), nested_stack.state) self.m.VerifyAll()
def test_nested_stack_create_with_timeout(self): urlfetch.get('https://server.test/the.template').MultipleTimes().\ AndReturn(self.nested_template) self.m.ReplayAll() timeout_template = template_format.parse( copy.deepcopy(self.test_template)) props = timeout_template['Resources']['the_nested']['Properties'] props['TimeoutInMinutes'] = '50' stack = self.create_stack(json.dumps(timeout_template)) self.assertEqual(stack.state, (stack.CREATE, stack.COMPLETE)) self.m.VerifyAll()
def test_nested_stack_delete(self): urlfetch.get('https://server.test/the.template' ).MultipleTimes().AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack['the_nested'] scheduler.TaskRunner(rsrc.delete)() self.assertEqual((stack.DELETE, stack.COMPLETE), rsrc.state) nested_stack = parser.Stack.load(utils.dummy_context( 'test_username', 'aaaa', 'password'), rsrc.resource_id) self.assertEqual((stack.DELETE, stack.COMPLETE), nested_stack.state) self.m.VerifyAll()
def test_nested_stack_suspend_resume(self): urlfetch.get("https://localhost/the.template").AndReturn(self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack["the_nested"] scheduler.TaskRunner(rsrc.suspend)() self.assertEqual(rsrc.state, (rsrc.SUSPEND, rsrc.COMPLETE)) scheduler.TaskRunner(rsrc.resume)() self.assertEqual(rsrc.state, (rsrc.RESUME, rsrc.COMPLETE)) rsrc.delete() self.m.VerifyAll()
def test_get_template_resource_class(self): test_templ_name = "file:///etc/heatr/frodo.yaml" minimal_temp = json.dumps({"HeatTemplateFormatVersion": "2012-12-12", "Parameters": {}, "Resources": {}}) self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=("file",)).AndReturn(minimal_temp) self.m.ReplayAll() env_str = {"resource_registry": {"resources": {"fred": {"OS::ResourceType": test_templ_name}}}} env = environment.Environment(env_str) cls = env.get_class("OS::ResourceType", "fred") self.assertNotEqual(template_resource.TemplateResource, cls) self.assertTrue(issubclass(cls, template_resource.TemplateResource)) self.assertTrue(hasattr(cls, "properties_schema")) self.assertTrue(hasattr(cls, "attributes_schema")) self.m.VerifyAll()
def test_template_as_resource(self): """ Test that the resulting resource has the right prop and attrib schema. Note that this test requires the Wordpress_Single_Instance.yaml template in the templates directory since we want to test using a non-trivial template. """ test_templ_name = "WordPress_Single_Instance.yaml" path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates', test_templ_name) # check if its in the directory list vs. exists to work around # case-insensitive file systems self.assertIn(test_templ_name, os.listdir(os.path.dirname(path))) with open(path) as test_templ_file: test_templ = test_templ_file.read() self.assertTrue(test_templ, "Empty test template") self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('file',)).AndRaise(IOError) urlfetch.get(test_templ_name, allowed_schemes=('http', 'https')).AndReturn(test_templ) parsed_test_templ = template_format.parse(test_templ) self.m.ReplayAll() json_snippet = { "Type": test_templ_name, "Properties": { "KeyName": "mykeyname", "DBName": "wordpress1", "DBUsername": "******", "DBPassword": "******", "DBRootPassword": "******", "LinuxDistribution": "U10" } } stack = parser.Stack(utils.dummy_context(), 'test_stack', parser.Template({}), stack_id=str(uuid.uuid4())) templ_resource = resource.Resource("test_templ_resource", json_snippet, stack) self.m.VerifyAll() self.assertIsInstance(templ_resource, template_resource.TemplateResource) for prop in parsed_test_templ.get("Parameters", {}): self.assertIn(prop, templ_resource.properties) for attrib in parsed_test_templ.get("Outputs", {}): self.assertIn(attrib, templ_resource.attributes) for k, v in json_snippet.get("Properties").items(): self.assertEqual(v, templ_resource.properties[k])
def handle_create(self): template_data = urlfetch.get(self.properties[PROP_TEMPLATE_URL]) template = template_format.parse(template_data) return self.create_with_template(template, self.properties[PROP_PARAMETERS], self.properties[PROP_TIMEOUT_MINS])
def test_nested_stack_suspend_resume(self): urlfetch.get('https://server.test/the.template').AndReturn( self.nested_template) self.m.ReplayAll() stack = self.create_stack(self.test_template) rsrc = stack['the_nested'] scheduler.TaskRunner(rsrc.suspend)() self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state) scheduler.TaskRunner(rsrc.resume)() self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state) rsrc.delete() self.m.VerifyAll()
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: 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 template(self): """ Get template file contents, either inline, from stack adopt data or from a URL, in JSON or YAML format. """ if rpc_api.PARAM_ADOPT_STACK_DATA in self.data: adopt_data = self.data[rpc_api.PARAM_ADOPT_STACK_DATA] try: adopt_data = template_format.simple_parse(adopt_data) return adopt_data['template'] except (ValueError, KeyError) as ex: err_reason = _('Invalid adopt data: %s') % ex raise exc.HTTPBadRequest(err_reason) elif self.PARAM_TEMPLATE in self.data: template_data = self.data[self.PARAM_TEMPLATE] if isinstance(template_data, dict): return template_data elif self.PARAM_TEMPLATE_URL in self.data: url = self.data[self.PARAM_TEMPLATE_URL] LOG.debug('TemplateUrl %s' % url) try: template_data = urlfetch.get(url) except IOError as ex: err_reason = _('Could not retrieve template: %s') % ex raise exc.HTTPBadRequest(err_reason) else: raise exc.HTTPBadRequest(_("No template specified")) return self.format_parse(template_data, 'Template')
def test_file_scheme_supported(self): data = '{ "foo": "bar" }' url = 'file:///etc/profile' mock_open = self.patchobject(six.moves.urllib.request, 'urlopen') mock_open.return_value = six.moves.cStringIO(data) self.assertEqual(data, urlfetch.get(url, allowed_schemes=['file'])) mock_open.assert_called_once_with(url)
def test_file_scheme_supported(self): data = '{ "foo": "bar" }' url = 'file:///etc/profile' mock_open = self.patchobject(urllib.request, 'urlopen') mock_open.return_value = io.StringIO(data) self.assertEqual(data, urlfetch.get(url, allowed_schemes=['file'])) mock_open.assert_called_once_with(url)
def template(self): """ Get template file contents, either inline, from stack adopt data or from a URL, in JSON or YAML format. """ template_data = None if rpc_api.PARAM_ADOPT_STACK_DATA in self.data: adopt_data = self.data[rpc_api.PARAM_ADOPT_STACK_DATA] try: adopt_data = template_format.simple_parse(adopt_data) return adopt_data['template'] except (ValueError, KeyError) as ex: err_reason = _('Invalid adopt data: %s') % ex raise exc.HTTPBadRequest(err_reason) elif self.PARAM_TEMPLATE in self.data: template_data = self.data[self.PARAM_TEMPLATE] if isinstance(template_data, dict): return template_data elif self.PARAM_TEMPLATE_URL in self.data: url = self.data[self.PARAM_TEMPLATE_URL] LOG.debug('TemplateUrl %s' % url) try: template_data = urlfetch.get(url) except IOError as ex: err_reason = _('Could not retrieve template: %s') % ex raise exc.HTTPBadRequest(err_reason) if template_data is None: if self.patch: return None else: raise exc.HTTPBadRequest(_("No template specified")) with self.parse_error_check('Template'): return template_format.parse(template_data)
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: 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 test_template_as_resource(self): """ Test that the resulting resource has the right prop and attrib schema. Note that this test requires the Wordpress_Single_Instance.yaml template in the templates directory since we want to test using a non-trivial template. """ test_templ_name = "WordPress_Single_Instance.yaml" path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates', test_templ_name) # check if its in the directory list vs. exists to work around # case-insensitive file systems self.assertIn(test_templ_name, os.listdir(os.path.dirname(path))) with open(path) as test_templ_file: test_templ = test_templ_file.read() self.assertTrue(test_templ, "Empty test template") self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name).AndReturn(test_templ) parsed_test_templ = template_format.parse(test_templ) self.m.ReplayAll() json_snippet = { "Type": test_templ_name, "Properties": { "KeyName": "mykeyname", "DBName": "wordpress1", "DBUsername": "******", "DBPassword": "******", "DBRootPassword": "******", "LinuxDistribution": "U10" } } stack = parser.Stack(None, 'test_stack', parser.Template({}), stack_id=uuidutils.generate_uuid()) templ_resource = resource.Resource("test_templ_resource", json_snippet, stack) self.m.VerifyAll() self.assertIsInstance(templ_resource, template_resource.TemplateResource) for prop in parsed_test_templ.get("Parameters", {}): self.assertIn(prop, templ_resource.properties) for attrib in parsed_test_templ.get("Outputs", {}): self.assertIn(attrib, templ_resource.attributes) for k, v in json_snippet.get("Properties").items(): self.assertEqual(v, templ_resource.properties[k])
def test_https_scheme(self): url = 'https://example.com/template' data = b'{ "foo": "bar" }' response = Response(data) requests.get(url, stream=True).AndReturn(response) self.m.ReplayAll() self.assertEqual(data, urlfetch.get(url)) self.m.VerifyAll()
def get_template_file(template_name, allowed_schemes): try: return urlfetch.get(template_name, allowed_schemes=allowed_schemes) except (IOError, exceptions.RequestException) as r_exc: args = {'name': template_name, 'exc': six.text_type(r_exc)} msg = _('Could not fetch remote template ' '"%(name)s": %(exc)s') % args raise exception.NotFound(msg_fmt=msg)
def test_https_scheme(self): url = 'https://example.com/template' data = b'{ "foo": "bar" }' response = Response(data) mock_get = self.patchobject(requests, 'get') mock_get.return_value = response self.assertEqual(data, urlfetch.get(url)) mock_get.assert_called_once_with(url, stream=True)
def test_nested_stack_infinite_recursion(self): template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/the.template' ''' urlfetch.get('https://server.test/the.template').MultipleTimes( ).AndReturn(template) self.m.ReplayAll() t = template_format.parse(template) stack = self.parse_stack(t) res = self.assertRaises(exception.StackValidationFailed, stack.validate) self.assertIn('Recursion depth exceeds', six.text_type(res))
def test_https_scheme(self): url = 'https://example.com/template' data = '{ "foo": "bar" }' response = Response(data) requests.get(url, stream=True).AndReturn(response) self.m.ReplayAll() self.assertEqual(data, urlfetch.get(url)) self.m.VerifyAll()
def test_nested_stack_infinite_recursion(self): template = """ HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/the.template' """ urlfetch.get("https://server.test/the.template").MultipleTimes().AndReturn(template) self.m.ReplayAll() t = template_format.parse(template) stack = self.parse_stack(t) stack.create() self.assertEqual((stack.CREATE, stack.FAILED), stack.state) self.assertIn("Recursion depth exceeds", stack.status_reason) self.m.VerifyAll()
def test_nested_stack_infinite_recursion(self): template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/the.template' ''' urlfetch.get('https://server.test/the.template').MultipleTimes( ).AndReturn(template) self.m.ReplayAll() t = template_format.parse(template) stack = self.parse_stack(t) stack.create() self.assertEqual((stack.CREATE, stack.FAILED), stack.state) self.assertIn('Recursion depth exceeds', stack.status_reason) self.m.VerifyAll()
def test_https_scheme(self): url = 'https://example.com/template' data = '{ "foo": "bar" }' requests.get(url).AndReturn(Response(data)) self.m.ReplayAll() self.assertEqual(urlfetch.get(url), data) self.m.VerifyAll()
def test_https_scheme(self): url = 'https://example.com/template' data = '{ "foo": "bar" }' urllib2.urlopen(url).AndReturn(StringIO.StringIO(data)) self.m.ReplayAll() self.assertEqual(urlfetch.get(url), data) self.m.VerifyAll()
def test_nested_stack_four_deep(self): root_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth1.template' ''' depth1_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth2.template' ''' depth2_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth3.template' ''' depth3_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth4.template' Parameters: KeyName: foo ''' urlfetch.get( 'https://server.test/depth1.template').AndReturn( depth1_template) urlfetch.get( 'https://server.test/depth2.template').AndReturn( depth2_template) urlfetch.get( 'https://server.test/depth3.template').AndReturn( depth3_template) urlfetch.get( 'https://server.test/depth4.template').AndReturn( self.nested_template) self.m.ReplayAll() t = template_format.parse(root_template) stack = self.parse_stack(t) stack.create() self.assertEqual((stack.CREATE, stack.FAILED), stack.state) self.assertIn('Recursion depth exceeds', stack.status_reason) self.m.VerifyAll()
def test_nested_stack_four_deep(self): root_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth1.template' ''' depth1_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth2.template' ''' depth2_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth3.template' ''' depth3_template = ''' HeatTemplateFormatVersion: 2012-12-12 Resources: Nested: Type: AWS::CloudFormation::Stack Properties: TemplateURL: 'https://server.test/depth4.template' Parameters: KeyName: foo ''' urlfetch.get( 'https://server.test/depth1.template').AndReturn( depth1_template) urlfetch.get( 'https://server.test/depth2.template').AndReturn( depth2_template) urlfetch.get( 'https://server.test/depth3.template').AndReturn( depth3_template) urlfetch.get( 'https://server.test/depth4.template').AndReturn( self.nested_template) self.m.ReplayAll() t = template_format.parse(root_template) stack = self.parse_stack(t) res = self.assertRaises(exception.StackValidationFailed, stack.validate) self.assertIn('Recursion depth exceeds', six.text_type(res)) self.m.VerifyAll()