Files
tracer-cloud--opensre/tests/e2e/upstream_apache_flink_ecs/infrastructure_sdk/destroy.py
T
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

227 lines
8.7 KiB
Python

#!/usr/bin/env python3
"""Destroy Flink ECS infrastructure.
Uses tag-based cleanup to find and delete all resources created by the SDK deployment.
"""
import time
from contextlib import suppress
from tests.shared.infrastructure_sdk import destroy_stack, load_outputs
from tests.shared.infrastructure_sdk.config import delete_outputs
from tests.shared.infrastructure_sdk.deployer import get_boto3_client
from tests.shared.infrastructure_sdk.resources import ecr, ecs, iam, lambda_, logs, s3, vpc
STACK_NAME = "tracer-flink-ecs"
REGION = "us-east-1"
def destroy() -> dict:
"""Destroy all infrastructure resources."""
start_time = time.time()
print(f"Destroying stack: {STACK_NAME}")
print("=" * 60)
results = {"deleted": [], "failed": [], "not_found": []}
# Try to load outputs for explicit resource deletion
try:
outputs = load_outputs(STACK_NAME)
print("Found outputs file, performing explicit deletion...")
_explicit_delete(outputs, results)
except FileNotFoundError:
print("No outputs file found, using tag-based discovery...")
# Also run tag-based cleanup to catch any resources we might have missed
print("\n[Tag-Based Cleanup]")
tag_results = destroy_stack(STACK_NAME, REGION)
# Merge results
results["deleted"].extend(tag_results.get("deleted", []))
results["failed"].extend(tag_results.get("failed", []))
results["not_found"].extend(tag_results.get("not_found", []))
# Delete outputs file
with suppress(Exception):
delete_outputs(STACK_NAME)
elapsed = time.time() - start_time
print("\n" + "=" * 60)
print(f"Destruction completed in {elapsed:.1f}s")
print("=" * 60)
print(f"\nDeleted: {len(results['deleted'])} resources")
print(f"Failed: {len(results['failed'])} resources")
print(f"Not Found: {len(results['not_found'])} resources")
if results["failed"]:
print("\nFailed resources:")
for item in results["failed"]:
if isinstance(item, dict):
print(f" - {item.get('arn', 'unknown')}: {item.get('error', 'unknown error')}")
else:
print(f" - {item}")
return results
def _explicit_delete(outputs: dict, results: dict) -> None:
"""Delete resources explicitly using outputs."""
# Order: APIs -> Lambdas -> Task Definitions -> Clusters -> IAM -> S3 -> ECR -> Logs -> Security Groups
# 1. Delete API Gateways
print("\n[1/9] Deleting API Gateways...")
for api_key in ["TriggerApiUrl", "MockApiUrl"]:
url = outputs.get(api_key, "")
if url:
# Extract API ID from URL: https://xxx.execute-api.region.amazonaws.com/prod
try:
api_id = url.split("//")[1].split(".")[0]
print(f" - Deleting API: {api_id}")
api_client = get_boto3_client("apigateway", REGION)
api_client.delete_rest_api(restApiId=api_id)
results["deleted"].append(f"apigateway:{api_id}")
except Exception as e:
if "NotFoundException" in str(e):
results["not_found"].append(f"apigateway:{api_id}")
else:
results["failed"].append({"arn": f"apigateway:{api_id}", "error": str(e)})
# 2. Delete Lambda functions
print("\n[2/9] Deleting Lambda functions...")
lambda_names = [
outputs.get("TriggerLambdaName"),
outputs.get("MockApiLambdaName"),
]
for name in lambda_names:
if name:
try:
print(f" - Deleting Lambda: {name}")
lambda_.delete_function(name, REGION)
results["deleted"].append(f"lambda:{name}")
except Exception as e:
if "ResourceNotFoundException" in str(e):
results["not_found"].append(f"lambda:{name}")
else:
results["failed"].append({"arn": f"lambda:{name}", "error": str(e)})
# 3. Deregister task definitions
print("\n[3/9] Deregistering task definitions...")
task_def_arn = outputs.get("TaskDefinitionArn")
if task_def_arn:
try:
print(f" - Deregistering: {task_def_arn}")
ecs.deregister_task_definition(task_def_arn, REGION)
results["deleted"].append(task_def_arn)
except Exception as e:
results["failed"].append({"arn": task_def_arn, "error": str(e)})
# 4. Delete ECS cluster
print("\n[4/9] Deleting ECS cluster...")
cluster_name = outputs.get("EcsClusterName")
if cluster_name:
try:
print(f" - Deleting cluster: {cluster_name}")
ecs.delete_cluster(cluster_name, REGION)
results["deleted"].append(f"ecs:cluster/{cluster_name}")
except Exception as e:
if "ClusterNotFoundException" in str(e):
results["not_found"].append(f"ecs:cluster/{cluster_name}")
else:
results["failed"].append({"arn": f"ecs:cluster/{cluster_name}", "error": str(e)})
# 5. Delete IAM roles
print("\n[5/9] Deleting IAM roles...")
role_names = [
f"{STACK_NAME}-task-role",
f"{STACK_NAME}-execution-role",
f"{STACK_NAME}-trigger-role",
f"{STACK_NAME}-mock-api-role",
]
for role_name in role_names:
try:
print(f" - Deleting role: {role_name}")
iam.delete_role(role_name, REGION)
results["deleted"].append(f"iam:role/{role_name}")
except Exception as e:
if "NoSuchEntity" in str(e):
results["not_found"].append(f"iam:role/{role_name}")
else:
results["failed"].append({"arn": f"iam:role/{role_name}", "error": str(e)})
# 6. Delete S3 buckets
print("\n[6/9] Deleting S3 buckets...")
bucket_names = [
outputs.get("LandingBucketName"),
outputs.get("ProcessedBucketName"),
]
for bucket_name in bucket_names:
if bucket_name:
try:
print(f" - Deleting bucket: {bucket_name}")
s3.delete_bucket(bucket_name, REGION)
results["deleted"].append(f"s3:{bucket_name}")
except Exception as e:
if "NoSuchBucket" in str(e):
results["not_found"].append(f"s3:{bucket_name}")
else:
results["failed"].append({"arn": f"s3:{bucket_name}", "error": str(e)})
# 7. Delete ECR repository
print("\n[7/9] Deleting ECR repository...")
ecr_uri = outputs.get("EcrRepositoryUri")
if ecr_uri:
# Extract repo name from URI: account.dkr.ecr.region.amazonaws.com/repo-name
repo_name = ecr_uri.split("/")[-1]
try:
print(f" - Deleting repository: {repo_name}")
ecr.delete_repository(repo_name, REGION)
results["deleted"].append(f"ecr:{repo_name}")
except Exception as e:
if "RepositoryNotFoundException" in str(e):
results["not_found"].append(f"ecr:{repo_name}")
else:
results["failed"].append({"arn": f"ecr:{repo_name}", "error": str(e)})
# 8. Delete CloudWatch log group
print("\n[8/9] Deleting CloudWatch log group...")
log_group_name = outputs.get("LogGroupName")
if log_group_name:
try:
print(f" - Deleting log group: {log_group_name}")
logs.delete_log_group(log_group_name, REGION)
results["deleted"].append(f"logs:{log_group_name}")
except Exception as e:
if "ResourceNotFoundException" in str(e):
results["not_found"].append(f"logs:{log_group_name}")
else:
results["failed"].append({"arn": f"logs:{log_group_name}", "error": str(e)})
# 9. Delete security group
print("\n[9/9] Deleting security group...")
sg_id = outputs.get("SecurityGroupId")
if sg_id:
# Wait a bit for ENIs to detach
time.sleep(5)
try:
print(f" - Deleting security group: {sg_id}")
vpc.delete_security_group(sg_id, REGION)
results["deleted"].append(f"ec2:security-group/{sg_id}")
except Exception as e:
if "InvalidGroup.NotFound" in str(e):
results["not_found"].append(f"ec2:security-group/{sg_id}")
elif "DependencyViolation" in str(e):
print(" Warning: Security group has dependencies, may need manual cleanup")
results["failed"].append(
{
"arn": f"ec2:security-group/{sg_id}",
"error": "DependencyViolation - ENIs still attached",
}
)
else:
results["failed"].append({"arn": f"ec2:security-group/{sg_id}", "error": str(e)})
if __name__ == "__main__":
destroy()