Skip to content

Commit c79a527

Browse files
update the e2e cleanup script to also delete container images (#9939)
1 parent 7f1743a commit c79a527

File tree

4 files changed

+122
-1
lines changed

4 files changed

+122
-1
lines changed

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/e2e/common.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import assert from "node:assert";
2+
import { spawnSync } from "node:child_process";
13
import { fetch } from "undici";
24
import type { RequestInit, Response } from "undici";
35

@@ -288,3 +290,70 @@ export const listCertificates = async () => {
288290
export const deleteCertificate = async (id: string) => {
289291
await apiFetch(`/mtls_certificates/${id}`, "DELETE");
290292
};
293+
294+
// Note: the container images functions below don't directly use the REST API since
295+
// they interact with the cloudflare images registry which has it's own
296+
// non-trivial auth mechanism, so instead of duplicating a bunch of logic
297+
// here we instead directly call wrangler
298+
//
299+
// TODO: Consider if it would make sense for all the functions here to use wrangler
300+
// directly, what is the advantage of hitting the REST API directly?
301+
302+
export const listE2eContainerImages = () => {
303+
const output = runWranglerCommand("wrangler containers images list", {
304+
// This operation can take literally minutes to run...
305+
timeout: 3 * 60_000,
306+
}).stdout;
307+
308+
return output
309+
.split("\n")
310+
.map((line) => {
311+
const match = line.match(
312+
/^(?<imageName>tmp-e2e-worker[a-z0-9-]+)\s+tmp-e2e$/
313+
);
314+
if (!match?.groups) {
315+
return null;
316+
}
317+
318+
return { name: match.groups.imageName, tag: "tmp-e2e" };
319+
})
320+
.filter(Boolean) as { name: string; tag: string }[];
321+
};
322+
323+
export const deleteContainerImage = (image: { name: string; tag: string }) => {
324+
const run = runWranglerCommand(
325+
`wrangler containers images delete ${image.name}:${image.tag}`
326+
);
327+
328+
return run.status === 0;
329+
};
330+
331+
function runWranglerCommand(
332+
wranglerCommand: string,
333+
{ timeout = 10_000 } = {}
334+
) {
335+
// Enforce a `wrangler` prefix to make commands clearer to read
336+
assert(
337+
wranglerCommand.startsWith("wrangler "),
338+
"Commands must start with `wrangler` (e.g. `wrangler dev`) but got " +
339+
wranglerCommand
340+
);
341+
342+
const { status, stdout, stderr, output } = spawnSync(
343+
"pnpm",
344+
[wranglerCommand],
345+
{
346+
shell: true,
347+
stdio: "pipe",
348+
encoding: "utf8",
349+
timeout,
350+
}
351+
);
352+
353+
return {
354+
status,
355+
stdout,
356+
stderr,
357+
output: output.filter((line) => line !== null).join("\n"),
358+
};
359+
}

tools/e2e/e2eCleanup.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import {
22
deleteCertificate,
33
deleteContainerApplication,
4+
deleteContainerImage,
45
deleteDatabase,
56
deleteHyperdriveConfig,
67
deleteKVNamespace,
78
deleteProject,
89
deleteWorker,
910
listCertificates,
11+
listE2eContainerImages,
1012
listHyperdriveConfigs,
1113
listTmpDatabases,
1214
listTmpE2EContainerApplications,
@@ -34,6 +36,24 @@ run().catch((e) => {
3436
async function run() {
3537
// KV namespaces don't have a creation timestamp, but deletion will fail if there is a worker bound to it
3638
// so delete these first to avoid interrupting running e2e jobs (unless you are very very unlucky)
39+
await deleteKVNamespaces();
40+
41+
await deleteProjects();
42+
43+
await deleteWorkers();
44+
45+
await deleteD1Databases();
46+
47+
await deleteHyperdriveConfigs();
48+
49+
await deleteMtlsCertificates();
50+
51+
await deleteContainerApplications();
52+
53+
deleteContainerImages();
54+
}
55+
56+
async function deleteKVNamespaces() {
3757
const kvNamespacesToDelete = await listTmpKVNamespaces();
3858
for (const kvNamespace of kvNamespacesToDelete) {
3959
console.log("Deleting KV namespace: " + kvNamespace.title);
@@ -45,7 +65,9 @@ async function run() {
4565
if (kvNamespacesToDelete.length === 0) {
4666
console.log(`No KV namespaces to delete.`);
4767
}
68+
}
4869

70+
async function deleteProjects() {
4971
const projectsToDelete = await listTmpE2EProjects();
5072

5173
for (const project of projectsToDelete) {
@@ -58,7 +80,24 @@ async function run() {
5880
if (projectsToDelete.length === 0) {
5981
console.log(`No projects to delete.`);
6082
}
83+
}
84+
85+
function deleteContainerImages() {
86+
const containerImagesToDelete = listE2eContainerImages();
87+
88+
for (const image of containerImagesToDelete) {
89+
console.log("Deleting Container image: " + image.name + ":" + image.tag);
90+
deleteContainerImage(image)
91+
? console.log(`Successfully deleted project ${image.name}:${image.tag}`)
92+
: console.log(`Successfully deleted project ${image.name}:${image.tag}`);
93+
}
94+
95+
if (containerImagesToDelete.length === 0) {
96+
console.log(`No container image to delete.`);
97+
}
98+
}
6199

100+
async function deleteWorkers() {
62101
const workersToDelete = await listTmpE2EWorkers();
63102

64103
for (const worker of workersToDelete) {
@@ -71,8 +110,11 @@ async function run() {
71110
if (workersToDelete.length === 0) {
72111
console.log(`No workers to delete.`);
73112
}
113+
}
74114

115+
async function deleteD1Databases() {
75116
const d1DatabasesToDelete = await listTmpDatabases();
117+
76118
for (const db of d1DatabasesToDelete) {
77119
console.log("Deleting D1 database: " + db.name);
78120
(await deleteDatabase(db.uuid))
@@ -82,7 +124,9 @@ async function run() {
82124
if (d1DatabasesToDelete.length === 0) {
83125
console.log(`No D1 databases to delete.`);
84126
}
127+
}
85128

129+
async function deleteHyperdriveConfigs() {
86130
const hyperdriveConfigsToDelete = await listHyperdriveConfigs();
87131
for (const config of hyperdriveConfigsToDelete) {
88132
console.log("Deleting Hyperdrive configs: " + config.id);
@@ -94,7 +138,9 @@ async function run() {
94138
if (hyperdriveConfigsToDelete.length === 0) {
95139
console.log(`No Hyperdrive configs to delete.`);
96140
}
141+
}
97142

143+
async function deleteMtlsCertificates() {
98144
const mtlsCertificates = await listCertificates();
99145
for (const certificate of mtlsCertificates) {
100146
console.log("Deleting mTLS certificate: " + certificate.id);
@@ -107,7 +153,9 @@ async function run() {
107153
`Successfully deleted ${mtlsCertificates.length} mTLS certificates`
108154
);
109155
}
156+
}
110157

158+
async function deleteContainerApplications() {
111159
const containers = await listTmpE2EContainerApplications();
112160
for (const container of containers) {
113161
await deleteContainerApplication(container);

tools/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@typescript-eslint/parser": "^6.9.0",
1616
"find-up": "^6.3.0",
1717
"ts-dedent": "^2.2.0",
18-
"undici": "catalog:default"
18+
"undici": "catalog:default",
19+
"wrangler": "workspace:*"
1920
}
2021
}

0 commit comments

Comments
 (0)