Move dbinstance into a TemplateResource
The old dbinstance will soon be overtaken by the trove resource. - The only reason for keeping this around is for people that don't have trove installed and want to use the AWS resource. - Seperating it out into a TemplateResource really makes it easier for deployers and users to customise it. - The old dbinstance did nothing "special" in python, and was really one of the first "TemplateResources" as it attempted to convert properties into parameters. Since this in now done a lot better in the TemplateResource lets just make use of that. This will make it easier to migrate to other distros and versions. Partial-Bug: #1215797 Change-Id: If72e1f40f67dc831551e0db8df8caaa002aaaeda
This commit is contained in:
parent
5fc4d75cf5
commit
13b7f54ad8
|
@ -6,3 +6,4 @@ resource_registry:
|
||||||
#"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
|
#"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
|
||||||
"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm"
|
"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm"
|
||||||
"OS::Metering::Alarm": "OS::Ceilometer::Alarm"
|
"OS::Metering::Alarm": "OS::Ceilometer::Alarm"
|
||||||
|
"AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml"
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
HeatTemplateFormatVersion: '2012-12-12'
|
||||||
|
Description: 'Builtin AWS::RDS::DBInstance'
|
||||||
|
Parameters:
|
||||||
|
AllocatedStorage:
|
||||||
|
Type: String
|
||||||
|
DBInstanceClass:
|
||||||
|
Type: String
|
||||||
|
DBName:
|
||||||
|
Type: String
|
||||||
|
DBSecurityGroups:
|
||||||
|
Type: CommaDelimitedList
|
||||||
|
Default: ''
|
||||||
|
Engine:
|
||||||
|
Type: String
|
||||||
|
AllowedValues: ['MySQL']
|
||||||
|
MasterUsername:
|
||||||
|
Type: String
|
||||||
|
MasterUserPassword:
|
||||||
|
Type: String
|
||||||
|
Port:
|
||||||
|
Type: String
|
||||||
|
Default: '3306'
|
||||||
|
KeyName:
|
||||||
|
Type: String
|
||||||
|
Default: ''
|
||||||
|
|
||||||
|
Mappings:
|
||||||
|
DBInstanceToInstance:
|
||||||
|
db.m1.small: {Instance: m1.small}
|
||||||
|
db.m1.large: {Instance: m1.large}
|
||||||
|
db.m1.xlarge: {Instance: m1.xlarge}
|
||||||
|
db.m2.xlarge: {Instance: m2.xlarge}
|
||||||
|
db.m2.2xlarge: {Instance: m2.2xlarge}
|
||||||
|
db.m2.4xlarge: {Instance: m2.4xlarge}
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
DatabaseInstance:
|
||||||
|
Type: AWS::EC2::Instance
|
||||||
|
Metadata:
|
||||||
|
AWS::CloudFormation::Init:
|
||||||
|
config:
|
||||||
|
packages:
|
||||||
|
yum:
|
||||||
|
mysql : []
|
||||||
|
mysql-server : []
|
||||||
|
services:
|
||||||
|
systemd:
|
||||||
|
mysqld:
|
||||||
|
enabled: true
|
||||||
|
ensureRunning: true
|
||||||
|
Properties:
|
||||||
|
ImageId: F17-x86_64-cfntools
|
||||||
|
InstanceType: {'Fn::FindInMap': [DBInstanceToInstance,
|
||||||
|
{Ref: DBInstanceClass}, Instance]}
|
||||||
|
KeyName: {Ref: KeyName}
|
||||||
|
|
||||||
|
UserData:
|
||||||
|
Fn::Base64:
|
||||||
|
Fn::Replace:
|
||||||
|
- 'AWS::StackName': {Ref: 'AWS::StackName'}
|
||||||
|
'AWS::Region': {Ref: 'AWS::Region'}
|
||||||
|
MasterUsername: {Ref: MasterUsername}
|
||||||
|
MasterUserPassword: {Ref: MasterUserPassword}
|
||||||
|
DBName: {Ref: DBName}
|
||||||
|
WaitHandle: {Ref: WaitHandle}
|
||||||
|
- |
|
||||||
|
#!/bin/bash -v
|
||||||
|
# Helper function
|
||||||
|
function error_exit
|
||||||
|
{
|
||||||
|
/opt/aws/bin/cfn-signal -e 1 -r \"$1\" 'WaitHandle'
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
/opt/aws/bin/cfn-init -s AWS::StackName -r DatabaseInstance --region AWS::Region || error_exit 'Failed to run cfn-init'
|
||||||
|
# Setup MySQL root password and create a user
|
||||||
|
mysqladmin -u root password 'MasterUserPassword'
|
||||||
|
cat << EOF | mysql -u root --password='MasterUserPassword'
|
||||||
|
CREATE DATABASE DBName;
|
||||||
|
GRANT ALL PRIVILEGES ON DBName.* TO "MasterUsername"@"%"
|
||||||
|
IDENTIFIED BY "MasterUserPassword";
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
EXIT
|
||||||
|
EOF
|
||||||
|
# Database setup completed, signal success
|
||||||
|
/opt/aws/bin/cfn-signal -e 0 -r "MySQL server setup complete" 'WaitHandle'
|
||||||
|
|
||||||
|
WaitHandle:
|
||||||
|
Type: AWS::CloudFormation::WaitConditionHandle
|
||||||
|
WaitCondition:
|
||||||
|
Type: AWS::CloudFormation::WaitCondition
|
||||||
|
DependsOn: DatabaseInstance
|
||||||
|
Properties:
|
||||||
|
Handle: {Ref: WaitHandle}
|
||||||
|
Timeout: "600"
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
Endpoint.Address: {'Fn::GetAtt': [DatabaseInstance, PublicIp]}
|
||||||
|
Endpoint.Port: {Ref: Port}
|
|
@ -1,244 +0,0 @@
|
||||||
# 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.
|
|
||||||
|
|
||||||
from heat.common import template_format
|
|
||||||
from heat.engine import stack_resource
|
|
||||||
from heat.openstack.common import log as logging
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
mysql_template = r'''
|
|
||||||
{
|
|
||||||
"AWSTemplateFormatVersion": "2010-09-09",
|
|
||||||
"Description": "Builtin RDS::DBInstance",
|
|
||||||
"Parameters" : {
|
|
||||||
"DBInstanceClass" : {
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
|
|
||||||
"DBName" : {
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
|
|
||||||
"MasterUsername" : {
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
|
|
||||||
"MasterUserPassword" : {
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
|
|
||||||
"AllocatedStorage" : {
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
|
|
||||||
"DBSecurityGroups" : {
|
|
||||||
"Type": "CommaDelimitedList",
|
|
||||||
"Default": ""
|
|
||||||
},
|
|
||||||
|
|
||||||
"Port" : {
|
|
||||||
"Type": "String"
|
|
||||||
},
|
|
||||||
|
|
||||||
"KeyName" : {
|
|
||||||
"Type" : "String"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"Mappings" : {
|
|
||||||
"DBInstanceToInstance" : {
|
|
||||||
"db.m1.small": {"Instance": "m1.small"},
|
|
||||||
"db.m1.large": {"Instance": "m1.large"},
|
|
||||||
"db.m1.xlarge": {"Instance": "m1.xlarge"},
|
|
||||||
"db.m2.xlarge": {"Instance": "m2.xlarge"},
|
|
||||||
"db.m2.2xlarge": {"Instance": "m2.2xlarge"},
|
|
||||||
"db.m2.4xlarge": {"Instance": "m2.4xlarge"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
"Resources": {
|
|
||||||
"DatabaseInstance": {
|
|
||||||
"Type": "AWS::EC2::Instance",
|
|
||||||
"Metadata": {
|
|
||||||
"AWS::CloudFormation::Init": {
|
|
||||||
"config": {
|
|
||||||
"packages": {
|
|
||||||
"yum": {
|
|
||||||
"mysql" : [],
|
|
||||||
"mysql-server" : []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"systemd": {
|
|
||||||
"mysqld" : { "enabled" : "true", "ensureRunning" : "true" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Properties": {
|
|
||||||
"ImageId": "F17-x86_64-cfntools",
|
|
||||||
"InstanceType": { "Fn::FindInMap": [ "DBInstanceToInstance",
|
|
||||||
{ "Ref": "DBInstanceClass" },
|
|
||||||
"Instance" ] },
|
|
||||||
"KeyName": { "Ref": "KeyName" },
|
|
||||||
"UserData": { "Fn::Base64": { "Fn::Join": ["", [
|
|
||||||
"#!/bin/bash -v\n",
|
|
||||||
"# Helper function\n",
|
|
||||||
"function error_exit\n",
|
|
||||||
"{\n",
|
|
||||||
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '",
|
|
||||||
{ "Ref" : "WaitHandle" }, "'\n",
|
|
||||||
" exit 1\n",
|
|
||||||
"}\n",
|
|
||||||
|
|
||||||
"/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackName" },
|
|
||||||
" -r DatabaseInstance",
|
|
||||||
" --region ", { "Ref" : "AWS::Region" },
|
|
||||||
" || error_exit 'Failed to run cfn-init'\n",
|
|
||||||
"# Setup MySQL root password and create a user\n",
|
|
||||||
"mysqladmin -u root password '", {"Ref":"MasterUserPassword"},"'\n",
|
|
||||||
"cat << EOF | mysql -u root --password='",
|
|
||||||
{ "Ref" : "MasterUserPassword" }, "'\n",
|
|
||||||
"CREATE DATABASE ", { "Ref" : "DBName" }, ";\n",
|
|
||||||
"GRANT ALL PRIVILEGES ON ", { "Ref" : "DBName" },
|
|
||||||
".* TO \"", { "Ref" : "MasterUsername" }, "\"@\"%\"\n",
|
|
||||||
"IDENTIFIED BY \"", { "Ref" : "MasterUserPassword" }, "\";\n",
|
|
||||||
"FLUSH PRIVILEGES;\n",
|
|
||||||
"EXIT\n",
|
|
||||||
"EOF\n",
|
|
||||||
"# Database setup completed, signal success\n",
|
|
||||||
"/opt/aws/bin/cfn-signal -e 0 -r \"MySQL server setup complete\" '",
|
|
||||||
{ "Ref" : "WaitHandle" }, "'\n"
|
|
||||||
|
|
||||||
]]}}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"WaitHandle" : {
|
|
||||||
"Type" : "AWS::CloudFormation::WaitConditionHandle"
|
|
||||||
},
|
|
||||||
|
|
||||||
"WaitCondition" : {
|
|
||||||
"Type" : "AWS::CloudFormation::WaitCondition",
|
|
||||||
"DependsOn" : "DatabaseInstance",
|
|
||||||
"Properties" : {
|
|
||||||
"Handle" : {"Ref" : "WaitHandle"},
|
|
||||||
"Timeout" : "600"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"Outputs": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
class DBInstance(stack_resource.StackResource):
|
|
||||||
|
|
||||||
properties_schema = {
|
|
||||||
'DBSnapshotIdentifier': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'AllocatedStorage': {'Type': 'String',
|
|
||||||
'Required': True},
|
|
||||||
'AvailabilityZone': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'BackupRetentionPeriod': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'DBInstanceClass': {'Type': 'String',
|
|
||||||
'Required': True},
|
|
||||||
'DBName': {'Type': 'String',
|
|
||||||
'Required': False},
|
|
||||||
'DBParameterGroupName': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'DBSecurityGroups': {'Type': 'List',
|
|
||||||
'Required': False, 'Default': []},
|
|
||||||
'DBSubnetGroupName': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'Engine': {'Type': 'String',
|
|
||||||
'AllowedValues': ['MySQL'],
|
|
||||||
'Required': True},
|
|
||||||
'EngineVersion': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'LicenseModel': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'MasterUsername': {'Type': 'String',
|
|
||||||
'Required': True},
|
|
||||||
'MasterUserPassword': {'Type': 'String',
|
|
||||||
'Required': True},
|
|
||||||
'Port': {'Type': 'String',
|
|
||||||
'Default': '3306',
|
|
||||||
'Required': False},
|
|
||||||
'PreferredBackupWindow': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'PreferredMaintenanceWindow': {'Type': 'String',
|
|
||||||
'Implemented': False},
|
|
||||||
'MultiAZ': {'Type': 'Boolean',
|
|
||||||
'Implemented': False},
|
|
||||||
}
|
|
||||||
|
|
||||||
# We only support a couple of the attributes right now
|
|
||||||
attributes_schema = {
|
|
||||||
"Endpoint.Address": "Connection endpoint for the database.",
|
|
||||||
"Endpoint.Port": ("The port number on which the database accepts "
|
|
||||||
"connections.")
|
|
||||||
}
|
|
||||||
|
|
||||||
def _params(self):
|
|
||||||
params = {
|
|
||||||
'KeyName': {'Ref': 'KeyName'},
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add the DBInstance parameters specified in the user's template
|
|
||||||
# Ignore the not implemented ones
|
|
||||||
for key, value in self.properties_schema.items():
|
|
||||||
if value.get('Implemented', True) and key != 'Engine':
|
|
||||||
# There is a mismatch between the properties "List" format
|
|
||||||
# and the parameters "CommaDelimitedList" format, so we need
|
|
||||||
# to translate lists into the expected comma-delimited form
|
|
||||||
if isinstance(self.properties[key], list):
|
|
||||||
params[key] = ','.join(self.properties[key])
|
|
||||||
else:
|
|
||||||
params[key] = self.properties[key]
|
|
||||||
p = self.stack.resolve_static_data(params)
|
|
||||||
return p
|
|
||||||
|
|
||||||
def handle_create(self):
|
|
||||||
templ = template_format.parse(mysql_template)
|
|
||||||
return self.create_with_template(templ, self._params())
|
|
||||||
|
|
||||||
def handle_delete(self):
|
|
||||||
return self.delete_nested()
|
|
||||||
|
|
||||||
def _resolve_attribute(self, name):
|
|
||||||
'''
|
|
||||||
We don't really support any of these yet.
|
|
||||||
'''
|
|
||||||
if name == 'Endpoint.Address':
|
|
||||||
if self.nested() and 'DatabaseInstance' in self.nested().resources:
|
|
||||||
return self.nested().resources['DatabaseInstance']._ipaddress()
|
|
||||||
else:
|
|
||||||
return '0.0.0.0'
|
|
||||||
elif name == 'Endpoint.Port':
|
|
||||||
return self.properties['Port']
|
|
||||||
|
|
||||||
|
|
||||||
def resource_mapping():
|
|
||||||
return {
|
|
||||||
'AWS::RDS::DBInstance': DBInstance,
|
|
||||||
}
|
|
|
@ -12,15 +12,11 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import mox
|
|
||||||
|
|
||||||
from heat.common import exception
|
|
||||||
from heat.common import template_format
|
from heat.common import template_format
|
||||||
from heat.engine import scheduler
|
from heat.engine import resource
|
||||||
from heat.engine.resources import dbinstance as dbi
|
|
||||||
from heat.tests.common import HeatTestCase
|
from heat.tests.common import HeatTestCase
|
||||||
from heat.tests import utils
|
from heat.tests import utils
|
||||||
|
from heat.engine import parser
|
||||||
|
|
||||||
|
|
||||||
rds_template = '''
|
rds_template = '''
|
||||||
|
@ -52,64 +48,71 @@ rds_template = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class DBInstance(resource.Resource):
|
||||||
|
"""This is copied from the old DBInstance
|
||||||
|
to verify the schema of the new TemplateResource.
|
||||||
|
"""
|
||||||
|
properties_schema = {
|
||||||
|
'DBSnapshotIdentifier': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'AllocatedStorage': {'Type': 'String',
|
||||||
|
'Required': True},
|
||||||
|
'AvailabilityZone': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'BackupRetentionPeriod': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'DBInstanceClass': {'Type': 'String',
|
||||||
|
'Required': True},
|
||||||
|
'DBName': {'Type': 'String',
|
||||||
|
'Required': False},
|
||||||
|
'DBParameterGroupName': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'DBSecurityGroups': {'Type': 'List',
|
||||||
|
'Required': False, 'Default': []},
|
||||||
|
'DBSubnetGroupName': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'Engine': {'Type': 'String',
|
||||||
|
'AllowedValues': ['MySQL'],
|
||||||
|
'Required': True},
|
||||||
|
'EngineVersion': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'LicenseModel': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'MasterUsername': {'Type': 'String',
|
||||||
|
'Required': True},
|
||||||
|
'MasterUserPassword': {'Type': 'String',
|
||||||
|
'Required': True},
|
||||||
|
'Port': {'Type': 'String',
|
||||||
|
'Default': '3306',
|
||||||
|
'Required': False},
|
||||||
|
'PreferredBackupWindow': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'PreferredMaintenanceWindow': {'Type': 'String',
|
||||||
|
'Implemented': False},
|
||||||
|
'MultiAZ': {'Type': 'Boolean',
|
||||||
|
'Implemented': False},
|
||||||
|
}
|
||||||
|
|
||||||
|
# We only support a couple of the attributes right now
|
||||||
|
attributes_schema = {
|
||||||
|
"Endpoint.Address": "Connection endpoint for the database.",
|
||||||
|
"Endpoint.Port": ("The port number on which the database accepts "
|
||||||
|
"connections.")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DBInstanceTest(HeatTestCase):
|
class DBInstanceTest(HeatTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(DBInstanceTest, self).setUp()
|
super(DBInstanceTest, self).setUp()
|
||||||
utils.setup_dummy_db()
|
utils.setup_dummy_db()
|
||||||
self.m.StubOutWithMock(dbi.DBInstance, 'create_with_template')
|
|
||||||
self.m.StubOutWithMock(dbi.DBInstance, 'check_create_complete')
|
|
||||||
self.m.StubOutWithMock(dbi.DBInstance, 'nested')
|
|
||||||
|
|
||||||
def create_dbinstance(self, t, stack, resource_name):
|
|
||||||
resource = dbi.DBInstance(resource_name,
|
|
||||||
t['Resources'][resource_name],
|
|
||||||
stack)
|
|
||||||
self.assertEqual(None, resource.validate())
|
|
||||||
scheduler.TaskRunner(resource.create)()
|
|
||||||
self.assertEqual((resource.CREATE, resource.COMPLETE), resource.state)
|
|
||||||
return resource
|
|
||||||
|
|
||||||
def test_dbinstance(self):
|
def test_dbinstance(self):
|
||||||
|
"""test that the Template is parsable and
|
||||||
|
publishes the correct properties.
|
||||||
|
"""
|
||||||
|
templ = parser.Template(template_format.parse(rds_template))
|
||||||
|
stack = parser.Stack(utils.dummy_context(), 'test_stack',
|
||||||
|
templ)
|
||||||
|
|
||||||
class FakeDatabaseInstance(object):
|
res = stack['DatabaseServer']
|
||||||
def _ipaddress(self):
|
self.assertEquals(None, res._validate_against_facade(DBInstance))
|
||||||
return '10.0.0.1'
|
|
||||||
|
|
||||||
class FakeNested(object):
|
|
||||||
resources = {'DatabaseInstance': FakeDatabaseInstance()}
|
|
||||||
|
|
||||||
params = {'DBSecurityGroups': '',
|
|
||||||
'MasterUsername': u'admin',
|
|
||||||
'MasterUserPassword': u'admin',
|
|
||||||
'DBName': u'wordpress',
|
|
||||||
'KeyName': u'test',
|
|
||||||
'AllocatedStorage': u'5',
|
|
||||||
'DBInstanceClass': u'db.m1.small',
|
|
||||||
'Port': '3306'}
|
|
||||||
|
|
||||||
dbi.DBInstance.create_with_template(mox.IgnoreArg(),
|
|
||||||
params).AndReturn(None)
|
|
||||||
dbi.DBInstance.check_create_complete(mox.IgnoreArg()).AndReturn(True)
|
|
||||||
|
|
||||||
fn = FakeNested()
|
|
||||||
|
|
||||||
dbi.DBInstance.nested().AndReturn(None)
|
|
||||||
dbi.DBInstance.nested().MultipleTimes().AndReturn(fn)
|
|
||||||
self.m.ReplayAll()
|
|
||||||
|
|
||||||
t = template_format.parse(rds_template)
|
|
||||||
s = utils.parse_stack(t)
|
|
||||||
resource = self.create_dbinstance(t, s, 'DatabaseServer')
|
|
||||||
|
|
||||||
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'))
|
|
||||||
|
|
||||||
try:
|
|
||||||
resource.FnGetAtt('foo')
|
|
||||||
except exception.InvalidTemplateAttribute:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise Exception('Expected InvalidTemplateAttribute')
|
|
||||||
|
|
||||||
self.m.VerifyAll()
|
|
||||||
|
|
Loading…
Reference in New Issue