Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Create dependency graph for stack deletion
Browse files Browse the repository at this point in the history
This implementation puts the dependencies in this order:

<almost everything> ScalingGroup -> RDSInstance (this takes the longest)
-> DB Sec Group -> VPC attachment -> InternetGateway -> VPC

```
DELETE_COMPLETE       AWS::IAM::InstanceProfile                          InstanceProfile-DELETE_COMPLETE-2016-06-21T23:29:31.854Z
DELETE_IN_PROGRESS    AWS::IAM::Role                                     BaseHostRole-DELETE_IN_PROGRESS-2016-06-21T23:29:34.051Z
DELETE_COMPLETE       AWS::IAM::Role                                     BaseHostRole-DELETE_COMPLETE-2016-06-21T23:29:34.661Z
DELETE_COMPLETE       AWS::EC2::SecurityGroup                            elb-DELETE_COMPLETE-2016-06-21T23:30:55.163Z
DELETE_COMPLETE       AWS::RDS::DBInstance                               RDSInstance-DELETE_COMPLETE-2016-06-21T23:31:12.548Z
DELETE_IN_PROGRESS    AWS::RDS::DBSubnetGroup                            RDSSubnetGroup-DELETE_IN_PROGRESS-2016-06-21T23:31:14.500Z
DELETE_COMPLETE       AWS::RDS::DBSubnetGroup                            RDSSubnetGroup-DELETE_COMPLETE-2016-06-21T23:31:15.324Z
DELETE_IN_PROGRESS    AWS::EC2::Subnet                                   SubnetC-DELETE_IN_PROGRESS-2016-06-21T23:31:17.022Z
DELETE_IN_PROGRESS    AWS::EC2::Subnet                                   SubnetB-DELETE_IN_PROGRESS-2016-06-21T23:31:17.103Z
DELETE_IN_PROGRESS    AWS::EC2::SecurityGroup                            DatabaseSG-DELETE_IN_PROGRESS-2016-06-21T23:31:17.224Z
DELETE_COMPLETE       AWS::EC2::SecurityGroup                            DatabaseSG-DELETE_COMPLETE-2016-06-21T23:31:18.963Z
DELETE_IN_PROGRESS    AWS::EC2::VPCGatewayAttachment                     AttachGateway-DELETE_IN_PROGRESS-2016-06-21T23:31:21.458Z
DELETE_IN_PROGRESS    AWS::EC2::Subnet                                   SubnetA-DELETE_IN_PROGRESS-2016-06-21T23:31:27.522Z
DELETE_COMPLETE       AWS::EC2::Subnet                                   SubnetC-DELETE_COMPLETE-2016-06-21T23:31:32.859Z
DELETE_COMPLETE       AWS::EC2::Subnet                                   SubnetB-DELETE_COMPLETE-2016-06-21T23:31:33.030Z
DELETE_COMPLETE       AWS::EC2::VPCGatewayAttachment                     AttachGateway-DELETE_COMPLETE-2016-06-21T23:31:37.421Z
DELETE_IN_PROGRESS    AWS::EC2::InternetGateway                          InternetGateway-DELETE_IN_PROGRESS-2016-06-21T23:31:39.451Z
DELETE_COMPLETE       AWS::EC2::Subnet                                   SubnetA-DELETE_COMPLETE-2016-06-21T23:31:43.439Z
DELETE_COMPLETE       AWS::EC2::InternetGateway                          InternetGateway-DELETE_COMPLETE-2016-06-21T23:31:55.217Z
DELETE_IN_PROGRESS    AWS::EC2::VPC                                      VPC-DELETE_IN_PROGRESS-2016-06-21T23:31:57.098Z
Stack successfully deleted
```

It also splits the test_rds test into two:
* *test_rds* which tests RDS with a basic VPC (no vpc template resources)
* *test_rds_with_vpc_dependencies* tests RDS with a VPC Gateway attachment
  • Loading branch information
filipposc5 committed Jul 1, 2016
1 parent 11b3fed commit ba6ecdc
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 4 deletions.
8 changes: 7 additions & 1 deletion bootstrap_cfn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,14 @@ def vpc(self):
Application=Ref("AWS::StackId"),
Network="Public",
),
DependsOn=vpc.title,
)

gw_attachment = VPCGatewayAttachment(
"AttachGateway",
VpcId=Ref(vpc),
InternetGatewayId=Ref(igw),
DependsOn=igw.title
)

route_table = RouteTable(
Expand Down Expand Up @@ -506,7 +508,6 @@ def rds(self, template):
DBSubnetGroupDescription="VPC Subnets"
)
resources.append(rds_subnet_group)

database_sg = SecurityGroup(
"DatabaseSG",
SecurityGroupIngress=[
Expand All @@ -525,6 +526,8 @@ def rds(self, template):
],
VpcId=Ref("VPC"),
GroupDescription="SG for EC2 Access to RDS",
# translates to DependsOn="AttachGateway",
DependsOn=[r.title for r in self._find_resources(template, "AWS::EC2::VPCGatewayAttachment")],
)
resources.append(database_sg)

Expand Down Expand Up @@ -1112,6 +1115,9 @@ def ec2(self):
LaunchConfigurationName=Ref(launch_config),
HealthCheckGracePeriod=health_check_grace_period,
HealthCheckType=health_check_type,
# should be equivalent to DependsOn= "AttachGateway",
DependsOn=[v.title for v in self.vpc() if 'AWS::EC2::VPCGatewayAttachment' == v.resource['Type']]

)
resources.append(scaling_group)

Expand Down
103 changes: 100 additions & 3 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ def test_custom_s3_policy(self):
compare(expected_outputs, actual_outputs)

def test_rds(self):

template = Template()
config = ConfigParser(
ProjectConfig(
'tests/sample-project.yaml',
'dev',
'tests/sample-project-passwords.yaml').config, 'my-stack-name')

db_sg = ec2.SecurityGroup('DatabaseSG')
db_sg.VpcId = Ref('VPC')
db_sg.GroupDescription = 'SG for EC2 Access to RDS'
Expand All @@ -307,6 +315,7 @@ def test_rds(self):
"IpProtocol": "tcp",
"CidrIp": FindInMap("SubnetConfig", "VPC", "CIDR")}
]
db_sg.DependsOn = []

db_subnet = rds.DBSubnetGroup('RDSSubnetGroup')
db_subnet.SubnetIds = [Ref('SubnetA'), Ref('SubnetB'), Ref('SubnetC')]
Expand All @@ -332,26 +341,113 @@ def test_rds(self):

known = [db_instance, db_subnet, db_sg]

config.rds(template)
resources = template.resources.values()
rds_dict = self._resources_to_dict(resources)
# RDS dict will contain DBIdentifier, which is random.
# So we check it seperately here then remove it
self.assertTrue("DBInstanceIdentifier" in rds_dict["RDSInstance"]["Properties"],
"test_rds: template does not contain DBInstanceIdentifier")
identifier = rds_dict["RDSInstance"]["Properties"]["DBInstanceIdentifier"]
# Identifier can be optionally be defined in the yaml template for compatibility.
# We're only testing the case where it's defined. If left undefined AWS will
# generate a random one.
self.assertEquals(identifier, 'test-dev')
rds_dict["RDSInstance"]["Properties"].pop("DBInstanceIdentifier")
known = self._resources_to_dict(known)
compare(known, rds_dict)

# Test for outputs
expected_outputs = {
"dbhost": {
"Description": "RDS Hostname",
"Value": {"Fn::GetAtt": ["RDSInstance", "Endpoint.Address"]}
},
"dbport": {
"Description": "RDS Port",
"Value": {
"Fn::GetAtt": ["RDSInstance", "Endpoint.Port"]
}
}
}
actual_outputs = self._resources_to_dict(template.outputs.values())
compare(expected_outputs, actual_outputs)

def test_rds_with_vpc_dependencies(self):
template = Template()

config = ConfigParser(
ProjectConfig(
'tests/sample-project.yaml',
'dev',
'tests/sample-project-passwords.yaml').config, 'my-stack-name')
# generate and add the VPCGatewayAttachment resource to the template
# to ensure it is passed as an attachment (DependsOn) later
vpc_resources_needed_for_rds = [
res for res in config.vpc()
if res.resource_type == 'AWS::EC2::VPCGatewayAttachment']
map(template.add_resource, vpc_resources_needed_for_rds)

db_sg = ec2.SecurityGroup('DatabaseSG')
db_sg.VpcId = Ref('VPC')
db_sg.GroupDescription = 'SG for EC2 Access to RDS'
db_sg.SecurityGroupIngress = [
{"ToPort": 5432,
"FromPort": 5432,
"IpProtocol": "tcp",
"CidrIp": FindInMap("SubnetConfig", "VPC", "CIDR")},
{"ToPort": 1433,
"FromPort": 1433,
"IpProtocol": "tcp",
"CidrIp": FindInMap("SubnetConfig", "VPC", "CIDR")},
{"ToPort": 3306,
"FromPort": 3306,
"IpProtocol": "tcp",
"CidrIp": FindInMap("SubnetConfig", "VPC", "CIDR")}
]
db_sg.DependsOn = ["AttachGateway"]

db_subnet = rds.DBSubnetGroup('RDSSubnetGroup')
db_subnet.SubnetIds = [Ref('SubnetA'), Ref('SubnetB'), Ref('SubnetC')]
db_subnet.DBSubnetGroupDescription = 'VPC Subnets'

db_instance = rds.DBInstance('RDSInstance', DependsOn=db_sg.title)
db_instance.MultiAZ = False
db_instance.MasterUsername = 'testuser'
db_instance.MasterUserPassword = 'testpassword'
db_instance.DBName = 'test'
db_instance.PubliclyAccessible = False
db_instance.StorageEncrypted = False
db_instance.StorageType = 'gp2'
db_instance.AllocatedStorage = 5
db_instance.AllowMajorVersionUpgrade = False
db_instance.AutoMinorVersionUpgrade = False
db_instance.BackupRetentionPeriod = 1
db_instance.DBInstanceClass = 'db.t2.micro'
db_instance.Engine = 'postgres'
db_instance.EngineVersion = '9.3.5'
db_instance.VPCSecurityGroups = [GetAtt(db_sg, 'GroupId')]
db_instance.DBSubnetGroupName = Ref(db_subnet)

known = [db_instance, db_subnet, db_sg]

template = Template()
config.rds(template)
resources = template.resources.values()
rds_dict = self._resources_to_dict(resources)
# RDS dict will contain DBIdentifier, which is random.
# So we check it seperately here then remove it
self.assertTrue("DBInstanceIdentifier" in rds_dict["RDSInstance"]["Properties"],
"test_rds: template does not contain DBInstanceIdentifier")
"test_rds_with_vpc_dependencies: "
"template does not contain DBInstanceIdentifier")
identifier = rds_dict["RDSInstance"]["Properties"]["DBInstanceIdentifier"]
# Identifier can be optionally be defined in the yaml template for compatibility.
# We're only testing the case where it's defined. If left undefined AWS will
# generate a random one.
self.assertEquals(identifier, 'test-dev')
rds_dict["RDSInstance"]["Properties"].pop("DBInstanceIdentifier")
# keep just the keys (rds) we want to compare,
# we are done with the vpc so pop the vpc gw attachment
[rds_dict.pop(res.title) for res in vpc_resources_needed_for_rds]
known = self._resources_to_dict(known)
compare(known, rds_dict)

Expand Down Expand Up @@ -1321,7 +1417,8 @@ def test_ec2(self):
LaunchConfigurationName=Ref("BaseHostLaunchConfig"),
AvailabilityZones=GetAZs(""),
HealthCheckGracePeriod=300,
HealthCheckType='EC2'
HealthCheckType='EC2',
DependsOn=['AttachGateway']
)

BaseHostSG = SecurityGroup(
Expand Down

0 comments on commit ba6ecdc

Please sign in to comment.