Coverage for scripts / live_release_validation / cleanup / retained.py: 100.00%
78 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
1"""Schedule and account for intentionally retained resources."""
3from __future__ import annotations
5import copy
6import json
7from typing import Any
9from botocore.exceptions import ClientError
11from ..cleanup.ecr import _cleanup_new_ecr_images, _cleanup_new_ecr_repositories
12from ..cleanup.log_groups import (
13 _cleanup_owned_log_groups,
14)
15from ..constants import (
16 _KMS_PENDING_WINDOW_DAYS,
17 _RUN_STACK_TAG,
18 _LogGroupCleanupError,
19)
20from ..models import RunContext, utc_now
21from ..ownership.kms import (
22 _kms_tags,
23 _validated_owned_kms_identity,
24)
27def _schedule_retained_kms_keys(ctx: RunContext) -> dict[str, Any]:
28 records = ctx.checkpoint.state.get("owned_kms_keys", [])
29 retained_records = [
30 record
31 for record in records
32 if _validated_owned_kms_identity(ctx, record)[3] == "harness-schedule"
33 ]
34 if retained_records and not ctx.settings.confirm_kms_key_deletion:
35 raise RuntimeError("Retained KMS keys exist but this identity did not confirm key deletion")
36 results: list[dict[str, Any]] = []
37 for record in records:
38 region, key_id, arn, cleanup_policy = _validated_owned_kms_identity(ctx, record)
39 record.setdefault("cleanup_policy", cleanup_policy)
40 kms = ctx.session.client("kms", region_name=region)
41 try:
42 metadata = kms.describe_key(KeyId=key_id).get("KeyMetadata", {})
43 except ClientError as exc:
44 if exc.response.get("Error", {}).get("Code") == "NotFoundException":
45 record["scheduled"] = True
46 record["deleted"] = True
47 results.append(
48 {
49 "arn": arn,
50 "cleanup_policy": cleanup_policy,
51 "already_absent": True,
52 }
53 )
54 ctx.persist()
55 continue
56 raise
57 if metadata.get("Arn") != arn:
58 raise RuntimeError(f"KMS key ARN changed for {key_id}")
59 tags = _kms_tags(kms, key_id)
60 if tags.get(_RUN_STACK_TAG) != record["run_tag"]:
61 raise RuntimeError(f"KMS run ownership changed for {arn}")
63 state = str(metadata.get("KeyState") or "")
64 if state != "PendingDeletion" and cleanup_policy == "harness-schedule":
65 if state not in {"Enabled", "Disabled"}:
66 raise RuntimeError(f"KMS key {arn} is {state}; refusing to schedule deletion")
67 kms.schedule_key_deletion(
68 KeyId=key_id,
69 PendingWindowInDays=_KMS_PENDING_WINDOW_DAYS,
70 )
71 metadata = kms.describe_key(KeyId=key_id).get("KeyMetadata", {})
72 state = str(metadata.get("KeyState") or "")
73 if state != "PendingDeletion":
74 raise RuntimeError(
75 f"Expected {cleanup_policy} KMS key {arn} to be PendingDeletion; found {state}"
76 )
77 deletion_date = metadata.get("DeletionDate")
78 record["scheduled"] = True
79 record["deletion_date"] = deletion_date.isoformat() if deletion_date is not None else None
80 if not record["deletion_date"]:
81 raise RuntimeError(f"Pending-deletion KMS key omitted its deletion date: {arn}")
82 results.append(
83 {
84 "arn": arn,
85 "state": state,
86 "cleanup_policy": cleanup_policy,
87 "deletion_date": record["deletion_date"],
88 }
89 )
90 ctx.persist()
91 return {
92 "keys": results,
93 "deletion_window": {
94 "harness_schedule_days": _KMS_PENDING_WINDOW_DAYS,
95 "cloudformation_delete": "observed per key deletion_date",
96 },
97 }
100def _retained_resource_cleanup(ctx: RunContext) -> dict[str, Any]:
101 result: dict[str, Any] = {"started_at": utc_now(), "errors": []}
102 try:
103 result["cloudwatch_logs"] = _cleanup_owned_log_groups(ctx)
104 except Exception as exc: # noqa: BLE001 - preserve partial evidence
105 if isinstance(exc, _LogGroupCleanupError):
106 result["cloudwatch_logs"] = copy.deepcopy(exc.details)
107 result["errors"].append(
108 {"phase": "cloudwatch-logs", "error": f"{type(exc).__name__}: {exc}"}
109 )
110 try:
111 result["ecr_images"] = _cleanup_new_ecr_images(ctx)
112 except Exception as exc: # noqa: BLE001 - preserve partial evidence
113 result["errors"].append({"phase": "ecr-images", "error": f"{type(exc).__name__}: {exc}"})
114 try:
115 result["ecr_repositories"] = _cleanup_new_ecr_repositories(ctx)
116 except Exception as exc: # noqa: BLE001 - preserve partial evidence
117 result["errors"].append(
118 {"phase": "ecr-repositories", "error": f"{type(exc).__name__}: {exc}"}
119 )
120 try:
121 result["kms"] = _schedule_retained_kms_keys(ctx)
122 except Exception as exc: # noqa: BLE001 - preserve partial evidence
123 result["errors"].append({"phase": "kms", "error": f"{type(exc).__name__}: {exc}"})
124 result["ended_at"] = utc_now()
125 ctx.checkpoint.state.setdefault("retained_cleanup_attempts", []).append(result)
126 ctx.persist()
127 if result["errors"]:
128 raise RuntimeError(
129 "Retained resource cleanup failed: " + json.dumps(result["errors"], sort_keys=True)
130 )
131 return result