| 1 | import * as ecs from "@distilled.cloud/aws/ecs"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | |
| 4 | /** |
| 5 | * Fully reclaim every revision of an out-of-band task definition family: |
| 6 | * deregister any ACTIVE revisions, then hard-delete the INACTIVE revisions |
| 7 | * via `deleteTaskDefinitions` (deregistering alone leaves them INACTIVE, |
| 8 | * which still shows up in a live `listTaskDefinitions --status INACTIVE` |
| 9 | * sweep). |
| 10 | * |
| 11 | * Idempotent — run it BEFORE a test to recover leftovers from a previously |
| 12 | * killed run (the family names are deterministic, so a re-run reclaims a |
| 13 | * prior orphan instead of stacking new revisions forever), and as a |
| 14 | * finalizer AFTER the test so a passing test leaves zero task definitions |
| 15 | * behind. |
| 16 | */ |
| 17 | export const reclaimTaskDefinitionFamily = (family: string) => |
| 18 | Effect.gen(function* () { |
| 19 | // `familyPrefix` is a prefix match — filter to the exact family so a |
| 20 | // family that happens to prefix another test's family is untouched. |
| 21 | const list = (status: "ACTIVE" | "INACTIVE") => |
| 22 | ecs |
| 23 | .listTaskDefinitions({ familyPrefix: family, status }) |
| 24 | .pipe( |
| 25 | Effect.map((r) => |
| 26 | (r.taskDefinitionArns ?? []).filter((arn) => |
| 27 | arn.includes(`/${family}:`), |
| 28 | ), |
| 29 | ), |
| 30 | ); |
| 31 | |
| 32 | const active = yield* list("ACTIVE"); |
| 33 | yield* Effect.forEach(active, (arn) => |
| 34 | ecs |
| 35 | .deregisterTaskDefinition({ taskDefinition: arn }) |
| 36 | .pipe(Effect.catchTag("ClientException", () => Effect.void)), |
| 37 | ); |
| 38 | |
| 39 | const inactive = yield* list("INACTIVE"); |
| 40 | // deleteTaskDefinitions accepts at most 10 ARNs per call. |
| 41 | for (let i = 0; i < inactive.length; i += 10) { |
| 42 | yield* ecs |
| 43 | .deleteTaskDefinitions({ taskDefinitions: inactive.slice(i, i + 10) }) |
| 44 | .pipe(Effect.catchTag("ClientException", () => Effect.void)); |
| 45 | } |
| 46 | }); |