diff --git a/aws/security-groups/deleteRabbitorySG.ts b/aws/security-groups/deleteRabbitorySG.ts index aa8d4f8..2ecd3b3 100644 --- a/aws/security-groups/deleteRabbitorySG.ts +++ b/aws/security-groups/deleteRabbitorySG.ts @@ -4,20 +4,20 @@ import { DeleteSecurityGroupCommand, } from "@aws-sdk/client-ec2"; -const GROUP_NAME = "RabbitorySG"; - -const isAwsError = (error: unknown): error is { name: string; message: string } => { - return typeof error === "object" && error !== null && "name" in error && "message" in error; -}; - -const getSecurityGroupId = async (client: EC2Client): Promise => { +const getRabbitorySGIds = async (client: EC2Client): Promise => { try { - const command = new DescribeSecurityGroupsCommand({ GroupNames: [GROUP_NAME] }); + const command = new DescribeSecurityGroupsCommand({}); const response = await client.send(command); - return response.SecurityGroups?.[0]?.GroupId ?? null; + const regex = /(^rabbitmq(-[a-z]+){4}$|^rabbitory)/i + return response.SecurityGroups?.reduce((ids, sg) => { + const groupName = sg.GroupName?.toLowerCase(); + if (sg.GroupId && groupName && regex.test(groupName)) { + ids.push(sg.GroupId); + } + return ids; + }, []) ?? []; } catch (error: unknown) { - if (isAwsError(error) && error.name === "InvalidGroup.NotFound") return null; // return if SG does not exist - throw new Error(`Error retrieving security group ID for ${GROUP_NAME}\n${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Error retrieving Rabbitory security groups\n${error instanceof Error ? error.message : String(error)}`); } }; @@ -37,10 +37,11 @@ export const deleteRabbitorySG = async (region: string): Promise => { const client = new EC2Client({ region: region }); try { - const securityGroupId = await getSecurityGroupId(client); - if (!securityGroupId) return; // return if no sg exists - await deleteSecurityGroup(securityGroupId, client); + const securityGroupIds = await getRabbitorySGIds(client); + for (const sgId of securityGroupIds) { + await deleteSecurityGroup(sgId, client); + } } catch (error: unknown) { - throw new Error(`Failed to delete RabbitorySG\n${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to delete Rabbitory security groups\n${error instanceof Error ? error.message : String(error)}`); } }; diff --git a/cli/destroy.ts b/cli/destroy.ts index f760d90..17b1cb5 100644 --- a/cli/destroy.ts +++ b/cli/destroy.ts @@ -8,55 +8,80 @@ import { getInstanceIdsByPublisher } from "./getInstanceIdsByPublisher"; import { runWithSpinner } from "./spinner"; import { getRegion } from "./getRegion"; import chalk from "chalk"; +import { fetchAllRegions } from "./fetchAllRegions"; + +const deleteAllBrokerInstances = async (regions: string[]) => { + for (const region of regions) { + const brokerIds = await getInstanceIdsByPublisher("Rabbitory", region); + if (brokerIds !== undefined) { + for (const brokerId of brokerIds) { + await deleteInstance(brokerId, region); + } + } + } +} + +const deleteAllSecurityGroups = async (regions: string[]) => { + for (const region of regions) { + await deleteRabbitorySG(region); + } +} export const destroy = async () => { try { const controlPanelName = "RabbitoryControlPanel"; - const region: string = await getRegion(); - const brokerIds = await getInstanceIdsByPublisher("Rabbitory", region); - const instanceIds: string[] | undefined = await getRunningInstanceIdsByName( - controlPanelName, - region, - ); - const instanceId: string | undefined = - instanceIds !== undefined && instanceIds.length > 0 - ? instanceIds[0] - : undefined; + const primaryRegion: string = await getRegion(); + + const regions = await fetchAllRegions(); + + if (!regions || regions.length === 0) { + console.error("Error fetching regions"); + process.exit(1); + } await runWithSpinner( "Deleting DynamoDB Table...", - () => deleteTable(region), + () => deleteTable(primaryRegion), "Deleted DynamoDB Table", ); - await runWithSpinner( - "Deleting RabbitMQ Broker Instances...", - () => - Promise.all(brokerIds?.map((id) => deleteInstance(id, region)) || []), - "Deleted RabbitMQ Broker Instances", + const instanceIds: string[] | undefined = await getRunningInstanceIdsByName( + controlPanelName, + primaryRegion, ); + const instanceId: string | undefined = + instanceIds !== undefined && instanceIds.length > 0 + ? instanceIds[0] + : undefined; if (instanceId !== undefined) { await runWithSpinner( "Terminating Control Panel EC2 instance...", - () => deleteInstance(instanceId, region), + () => deleteInstance(instanceId, primaryRegion), "Terminated EC2 instance", ); } + await runWithSpinner( + "Deleting RabbitMQ Broker Instances...", + () => deleteAllBrokerInstances(regions), + "Deleted RabbitMQ Broker Instances", + ); + await runWithSpinner( "Deleting Rabbitory security group...", - () => deleteRabbitorySG(region), + () => deleteAllSecurityGroups(regions), "Deleted Rabbitory security group", ); + await runWithSpinner( "Deleting RMQ Broker IAM role...", - () => deleteBrokerRole(region), + () => deleteBrokerRole(primaryRegion), "Deleted RMQ Broker IAM role", ); await runWithSpinner( "Deleting Rabbitory IAM role...", - () => deleteRabbitoryRole(region), + () => deleteRabbitoryRole(primaryRegion), "Deleted Rabbitory IAM role", ); } catch (error) { diff --git a/cli/fetchAllRegions.ts b/cli/fetchAllRegions.ts new file mode 100644 index 0000000..21c784d --- /dev/null +++ b/cli/fetchAllRegions.ts @@ -0,0 +1,15 @@ +import { EC2Client, DescribeRegionsCommand } from "@aws-sdk/client-ec2"; + +export const fetchAllRegions = async () => { + const client = new EC2Client({ region: "us-east-1" }); + + try { + const command = new DescribeRegionsCommand({}); + const response = await client.send(command); + const regions = response.Regions?.map(region => region.RegionName); + if (regions === undefined) throw new Error("Error fetching regions"); + return regions.filter((region) => region !== undefined); + } catch (error) { + console.error("Error fetching regions:", error); + } +};