raise NotImplementedError("Update not implemented for Resource %s"
% type(self))
- def metadata_update(self, metadata):
+ def metadata_update(self, new_metadata=None):
'''
No-op for resources which don't explicitly override this method
'''
- logger.warning("Resource %s does not implement metadata update" %
- self.name)
+ if new_metadata:
+ logger.warning("Resource %s does not implement metadata update" %
+ self.name)
class GenericResource(Resource):
return status
+ def metadata_update(self, new_metadata=None):
+ '''
+ Refresh the metadata if new_metadata is None
+ '''
+ if new_metadata is None:
+ self.metadata = self.parsed_template('Metadata')
+
def validate(self):
'''
Validate any of the provided params
if sorted(metadata.keys()) == expected_keys:
return metadata['Status'] in (SUCCESS, FAILURE)
- def metadata_update(self, metadata):
+ def metadata_update(self, new_metadata=None):
'''
Validate and update the resource metadata
'''
- if self._metadata_format_ok(metadata):
+ if new_metadata is None:
+ return
+
+ if self._metadata_format_ok(new_metadata):
rsrc_metadata = self.metadata
- if metadata['UniqueId'] in rsrc_metadata:
+ if new_metadata['UniqueId'] in rsrc_metadata:
logger.warning("Overwriting Metadata item for UniqueId %s!" %
- metadata['UniqueId'])
- new_metadata = {}
+ new_metadata['UniqueId'])
+ safe_metadata = {}
for k in ('Data', 'Reason', 'Status'):
- new_metadata[k] = metadata[k]
+ safe_metadata[k] = new_metadata[k]
# Note we can't update self.metadata directly, as it
# is a Metadata descriptor object which only supports get/set
- rsrc_metadata.update({metadata['UniqueId']: new_metadata})
+ rsrc_metadata.update({new_metadata['UniqueId']: safe_metadata})
self.metadata = rsrc_metadata
else:
logger.error("Metadata failed validation for %s" % self.name)
stack_name=stack.name)
resource = stack[resource_name]
- resource.metadata_update(metadata)
+ resource.metadata_update(new_metadata=metadata)
return resource.metadata
for action in actions:
action()
+ stk = parser.Stack.load(admin_context, stack=stack)
+ for res in stk:
+ res.metadata_update()
+
for wr in wrs:
rule = watchrule.WatchRule.load(stack_context, watch=wr)
actions = rule.evaluate()
self.fc.servers.list()[1])
#stack.Stack.create_with_template(mox.IgnoreArg()).AndReturn(None)
Metadata.__set__(mox.IgnoreArg(),
- mox.IgnoreArg()).AndReturn(None)
+ mox.IgnoreArg()).MultipleTimes().AndReturn(None)
lb.LoadBalancer.nova().MultipleTimes().AndReturn(self.fc)
self.m.ReplayAll()
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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 mox
+import uuid
+import time
+import datetime
+import json
+
+import eventlet
+import unittest
+from nose.plugins.attrib import attr
+
+from oslo.config import cfg
+
+from heat.tests import fakes
+from heat.tests.utils import stack_delete_after
+
+import heat.db as db_api
+from heat.common import template_format
+from heat.common import identifier
+from heat.engine import parser
+from heat.engine.resources import instance
+from heat.common import context
+
+test_template_metadata = '''
+{
+ "AWSTemplateFormatVersion" : "2010-09-09",
+ "Description" : "",
+ "Parameters" : {
+ "KeyName" : {"Type" : "String", "Default": "mine" },
+ },
+ "Resources" : {
+ "S1": {
+ "Type": "AWS::EC2::Instance",
+ "Metadata" : {
+ "AWS::CloudFormation::Init" : {
+ "config" : {
+ "files" : {
+ "/tmp/random_file" : {
+ "content" : { "Fn::Join" : ["", [
+ "s2-ip=", {"Fn::GetAtt": ["S2", "PublicIp"]}
+ ]]},
+ "mode" : "000400",
+ "owner" : "root",
+ "group" : "root"
+ }
+ }
+ }
+ }
+ },
+ "Properties": {
+ "ImageId" : "a",
+ "InstanceType" : "m1.large",
+ "KeyName" : { "Ref" : "KeyName" },
+ "UserData" : "#!/bin/bash -v\n"
+ }
+ },
+ "S2": {
+ "Type": "AWS::EC2::Instance",
+ "Properties": {
+ "ImageId" : "a",
+ "InstanceType" : "m1.large",
+ "KeyName" : { "Ref" : "KeyName" },
+ "UserData" : "#!/bin/bash -v\n"
+ }
+ }
+ }
+}
+'''
+
+
+@attr(tag=['unit', 'resource', 'Metadata'])
+@attr(speed='slow')
+class MetadataRefreshTest(unittest.TestCase):
+ '''
+ The point of the test is to confirm that metadata gets updated
+ when FnGetAtt() returns something different.
+ gets called.
+ '''
+ def setUp(self):
+ self.m = mox.Mox()
+ self.m.StubOutWithMock(eventlet, 'sleep')
+
+ self.fc = fakes.FakeKeystoneClient()
+
+ def tearDown(self):
+ self.m.UnsetStubs()
+
+ # Note tests creating a stack should be decorated with @stack_delete_after
+ # to ensure the stack is properly cleaned up
+ def create_stack(self, stack_name='test_stack',
+ template=test_template_metadata, params={},
+ stub=True):
+ temp = template_format.parse(template)
+ template = parser.Template(temp)
+ parameters = parser.Parameters(stack_name, template, params)
+ ctx = context.get_admin_context()
+ ctx.tenant_id = 'test_tenant'
+ stack = parser.Stack(ctx, stack_name, template, parameters,
+ disable_rollback=True)
+
+ self.stack_id = stack.store()
+
+ if stub:
+ self.m.StubOutWithMock(instance.Instance, 'handle_create')
+ instance.Instance.handle_create().MultipleTimes().AndReturn(None)
+ self.m.StubOutWithMock(instance.Instance, 'FnGetAtt')
+
+ return stack
+
+ @stack_delete_after
+ def test_FnGetAtt(self):
+ self.stack = self.create_stack()
+
+ instance.Instance.FnGetAtt('PublicIp').AndReturn('1.2.3.5')
+
+ # called by metadata_update()
+ instance.Instance.FnGetAtt('PublicIp').AndReturn('10.0.0.5')
+
+ self.m.ReplayAll()
+ self.stack.create()
+
+ s1 = self.stack.resources['S1']
+ s2 = self.stack.resources['S2']
+ files = s1.metadata['AWS::CloudFormation::Init']['config']['files']
+ cont = files['/tmp/random_file']['content']
+ self.assertEqual(cont, 's2-ip=1.2.3.5')
+
+ s1.metadata_update()
+ s2.metadata_update()
+ files = s1.metadata['AWS::CloudFormation::Init']['config']['files']
+ cont = files['/tmp/random_file']['content']
+ self.assertEqual(cont, 's2-ip=10.0.0.5')
+
+ self.m.VerifyAll()
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
- handle.metadata_update(test_metadata)
+ handle.metadata_update(new_metadata=test_metadata)
wc_att = resource.FnGetAtt('Data')
self.assertEqual(wc_att, '{"123": "foo"}')
test_metadata = {'Data': 'dog', 'Reason': 'cat',
'Status': 'SUCCESS', 'UniqueId': '456'}
- handle.metadata_update(test_metadata)
+ handle.metadata_update(new_metadata=test_metadata)
wc_att = resource.FnGetAtt('Data')
self.assertEqual(wc_att, u'{"123": "foo", "456": "dog"}')
self.m.VerifyAll()
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
- resource.metadata_update(test_metadata)
+ resource.metadata_update(new_metadata=test_metadata)
handle_metadata = {u'123': {u'Data': u'foo',
u'Reason': u'bar',
u'Status': u'SUCCESS'}}
# metadata_update should raise a ValueError if the metadata
# is missing any of the expected keys
err_metadata = {'Data': 'foo', 'Status': 'SUCCESS', 'UniqueId': '123'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
err_metadata = {'data': 'foo', 'reason': 'bar',
'status': 'SUCCESS', 'uniqueid': '1234'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
# Also any Status other than SUCCESS or FAILURE should be rejected
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'UCCESS', 'UniqueId': '123'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'wibble', 'UniqueId': '123'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'success', 'UniqueId': '123'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
err_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'FAIL', 'UniqueId': '123'}
- self.assertRaises(ValueError, resource.metadata_update, err_metadata)
+ self.assertRaises(ValueError, resource.metadata_update,
+ new_metadata=err_metadata)
self.m.VerifyAll()
@stack_delete_after
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
- resource.metadata_update(test_metadata)
+ resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status(), ['SUCCESS'])
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '456'}
- resource.metadata_update(test_metadata)
+ resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status(), ['SUCCESS', 'SUCCESS'])
# re-stub keystone() with fake client or stack delete fails
test_metadata = {'Data': 'foo', 'Reason': 'bar',
'Status': 'SUCCESS', 'UniqueId': '123'}
- resource.metadata_update(test_metadata)
+ resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status_reason('SUCCESS'), 'bar')
test_metadata = {'Data': 'dog', 'Reason': 'cat',
'Status': 'SUCCESS', 'UniqueId': '456'}
- resource.metadata_update(test_metadata)
+ resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status_reason('SUCCESS'), 'bar;cat')
test_metadata = {'Data': 'boo', 'Reason': 'hoo',
'Status': 'FAILURE', 'UniqueId': '789'}
- resource.metadata_update(test_metadata)
+ resource.metadata_update(new_metadata=test_metadata)
self.assertEqual(resource.get_status_reason('FAILURE'), 'hoo')
self.m.VerifyAll()