Skip to content

Commit

Permalink
start of work to handle stop for cleanup
Browse files Browse the repository at this point in the history
next I need to add a user identifier to the resources and
test the full pipeline

Signed-off-by: vsoch <vsoch@users.noreply.github.com>
  • Loading branch information
vsoch committed Jan 12, 2023
1 parent ad782e0 commit 89fc134
Showing 1 changed file with 145 additions and 17 deletions.
162 changes: 145 additions & 17 deletions playground/main/backends/aws/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,9 @@ def ensure_vpc(self, tutorial):
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.create_vpc
"""
response = self.ec2_client.describe_vpcs(
Filters=[{"Name": "tag:Name", "Values": [tutorial.slug]}]
)
if response["Vpcs"]:
return self.ec2_resources.Vpc(response["Vpcs"][0]["VpcId"])
vpc = self.get_vpc(tutorial)
if vpc:
return vpc

new_vpc = self.ec2_client.create_vpc(
CidrBlock=self.cidr_block, TagSpecifications=self.get_tags("vpc", tutorial)
Expand All @@ -74,6 +72,37 @@ def ensure_vpc(self, tutorial):
vpc.create_tags(Tags=[{"Key": "Name", "Value": tutorial.slug}])
return vpc

def delete_vpc(self, tutorial):
"""
Delete the vpc and subnets, etc.
"""
vpc = self.get_vpc(tutorial)
if not vpc:
logger.info("There is no VPC to delete.")
return

# Delete subnets
for subnet in vpc.subnets.iterator():
subnet.delete()

# Detach internet gateways
for gateway in vpc.internet_gateways.iterator():
vpc.detach_internet_gateway(InternetGatewayId=gateway.id)
gateway.delete()

# Delete routing tables and vpc (this seems to handle both)
vpc.delete()

def get_vpc(self, tutorial):
"""
Get a VPC with tags for the playground-tutorial
"""
response = self.ec2_client.describe_vpcs(
Filters=[{"Name": "tag:Name", "Values": [tutorial.slug]}]
)
if response["Vpcs"]:
return self.ec2_resources.Vpc(response["Vpcs"][0]["VpcId"])

def ensure_gateway(self, tutorial, vpc):
"""
Ensure we have an internet gateway.
Expand Down Expand Up @@ -181,24 +210,83 @@ def get_ami(self):
)
return details[0]["ImageId"]

def stop_instance(self, tutorial):
"""
Stop an instance.
"""
instances = self.get_instances(tutorial.slug)
if not instances:
logger.info("There are no instances to stop.")
return

# shutdown should terminate
ids = [x["InstanceId"] for x in instances]
self.ec2_client.stop_instances(InstanceIds=ids)

def stop_gateways(self, tutorial):
"""
Stop and delete internet gateways.
"""
gateways = self.get_gateways(tutorial.slug)
if not gateways:
logger.info("There are no gateways to stop.")
return
for gateway in gateways:
self.ec2_client.delete_internet_gateway(
InternetGatewayId=gateway["InternetGatewayId"]
)

def delete_security_group(self, tutorial):
"""
Clean up the security groups
"""
sgs = self.get_security_groups(tutorial.slug)
if not sgs:
logger.info("There are no security groups to delete.")
return

# We use the id because using group name assumes default VPC
for sg in sgs:
self.ec2_client.delete_security_group(GroupId=sg["GroupId"])

def delete_routing_tables(self, tutorial):
"""
Clean up routing tables
"""
tables = self.get_routing_tables(tutorial.slug)
if not tables:
logger.info("There are no routing tables to delete.")
return
for table in tables:
self.ec2_client.delete_route_table(RouteTableId=table["RouteTableId"])

def get_routing_tables(self, name):
"""
Get routing tables that match the playground-tutorial tags.
"""
tables = []
for table in self.ec2_client.describe_route_tables(
Filters=[{"Name": "tag:Name", "Values": [name]}]
).get("RouteTables", []):
print(table)
for tag in table.get("Tags", []):
if tag["Key"] == "Name" and tag["Value"] == name:
tables.append(table)
return tables

def stop(self, tutorial):
"""
Stop a tutorial
"""
# Figure out if it's already running
if not self.instance_exists(tutorial.slug):
logger.info(f"Instance {tutorial.slug} is not running.")
return
if self.instance_exists(tutorial.slug):
self.stop_instance(tutorial)

# zone = self.settings.get("zone") or self.zone
# Clean up security groups
self.delete_security_group(tutorial)

# Delete security group
try:
self.ec2_client.delete_security_group(
Filters=[{"Name": "group-name", "Values": [tutorial.slug]}]
)
except Exception:
pass
# This deletes routing table, vpc, and subnet
self.delete_vpc(tutorial)

def get_tags(self, resource, tutorial):
"""
Expand All @@ -214,6 +302,43 @@ def get_tags(self, resource, tutorial):
}
]

def get_gateways(self, name):
"""
Get all internet gateways with a playground-tutorial tag.
"""
gateways = []
for group in self.ec2_client.describe_internet_gateways().get(
"InternetGateways", []
):
for tag in group.get("Tags", []):
if tag["Key"] == "playground-tutorial" and tag["Value"] == name:
gateways.append(group)
return gateways

def get_security_groups(self, name):
"""
Get all security groups with a playground-tutorial tag.
"""
sgs = []
for group in self.ec2_client.describe_security_groups().get(
"SecurityGroups", []
):
if group["GroupName"] == name:
sgs.append(group)
return sgs

def get_instances(self, name):
"""
Get all instances with a playground-tutorial tag.
"""
instances = []
for group in self.list_instances().get("Reservations", {}):
for instance in group.get("Instances", []):
for tag in instance.get("Tags", []):
if tag["Key"] == "playground-tutorial" and tag["Value"] == name:
instances.append(instance)
return instances

def instance_exists(self, name):
"""
Determine if a tutorial instance exists.
Expand All @@ -228,6 +353,9 @@ def instance_exists(self, name):
def deploy(self, tutorial, envars=None):
"""
Deploy to AWS
This article was hugely helpful to get the ordering and relationship of things
correct! https://arjunmohnot.medium.com/aws-ec2-management-with-python-and-boto-3-59d849f1f58f
"""
# TODO cloud select should choose based on memory here
instance = self.settings["instance"]
Expand Down Expand Up @@ -259,14 +387,14 @@ def deploy(self, tutorial, envars=None):
}
]

# TODO we possibly should have key pair: https://www.learnaws.org/2020/12/16/aws-ec2-boto3-ultimate-guide/#how-to-create-an-ec2-key-pair
params = {
"ImageId": ami,
"InstanceType": instance,
"UserData": start_script,
"TagSpecifications": self.get_tags("instance", tutorial),
"KeyName": pem,
"NetworkInterfaces": networks,
"InstanceInitiatedShutdownBehavior": "terminate",
}

# Create the instance
Expand Down

0 comments on commit 89fc134

Please sign in to comment.