Last active
May 25, 2024 00:30
-
-
Save krasimir/8dd6f608fc829e5e5aa9ab0136452706 to your computer and use it in GitHub Desktop.
Deleting artifacts in GCP's Cloud Storage (ignores latest and leaves at least two versions)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const spawn = require("child_process").spawn; | |
const KEEP_AT_LEAST = 2; | |
const CONTAINER_REGISTRIES = [ | |
"gcr.io/<your project name>", | |
"eu.gcr.io/<your project name>/gcf/europe-west3" | |
]; | |
async function go(registry) { | |
console.log(`> ${registry}`); | |
const images = await command(`gcloud`, [ | |
"container", | |
"images", | |
"list", | |
`--repository=${registry}`, | |
"--format=json", | |
]); | |
for (let i = 0; i < images.length; i++) { | |
console.log(` ${images[i].name}`); | |
const image = images[i].name; | |
let tags = await command(`gcloud`, [ | |
"container", | |
"images", | |
"list-tags", | |
image, | |
"--format=json", | |
]); | |
const totalImages = tags.length; | |
// do not touch `latest` | |
tags = tags.filter(({ tags }) => !tags.find((tag) => tag === "latest")); | |
// sorting by date | |
tags.sort((a, b) => { | |
const d1 = new Date(a.timestamp.datetime); | |
const d2 = new Date(b.timestamp.datetime); | |
return d2.getTime() - d1.getTime(); | |
}); | |
// keeping at least X number of images | |
tags = tags.filter((_, i) => i >= KEEP_AT_LEAST); | |
console.log(` For removal: ${tags.length}/${totalImages}`); | |
for (let j = 0; j < tags.length; j++) { | |
console.log( | |
` Deleting: ${formatImageTimestamp(tags[j])} | ${tags[j].digest}` | |
); | |
await command("gcloud", [ | |
"container", | |
"images", | |
"delete", | |
`${image}@${tags[j].digest}`, | |
"--format=json", | |
"--quiet", | |
"--force-delete-tags", | |
]); | |
} | |
} | |
} | |
function command(cmd, args) { | |
return new Promise((done, reject) => { | |
const ps = spawn(cmd, args); | |
let result = ""; | |
ps.stdout.on("data", (data) => { | |
result += data; | |
}); | |
ps.stderr.on("data", (data) => { | |
result += data; | |
}); | |
ps.on("close", (code) => { | |
if (code !== 0) { | |
console.log(`process exited with code ${code}`); | |
} | |
try { | |
done(JSON.parse(result)); | |
} catch (err) { | |
done(result); | |
} | |
}); | |
}); | |
} | |
function formatImageTimestamp(image) { | |
const { year, month, day, hour, minute } = image.timestamp; | |
return `${year}-${month}-${day} ${hour}:${minute}`; | |
} | |
(async function () { | |
for (let i = 0; i < CONTAINER_REGISTRIES.length; i++) { | |
await go(CONTAINER_REGISTRIES[i]); | |
} | |
})(); |
I was thinking of running this script against my artifacts. One question I have though, why keep any beyond latest
? When I look at the Container Registry in GCP I only see 2 entries for each container, the latest
and another. Is it safe to delete the other by setting KEEP_AT_LEAST
to 0? With the way the script is written with the filters in place, it seems like if I set it to 0 the filter will keep the latest and get rid of the rest. Am I incorrect?
@Jmaiers yep, you are right. I guess I wanted to have an insurance in case of latest is messed up.
@Jmaiers yep, you are right. I guess I wanted to have an insurance in case of latest is messed up.
Excellent! Thank you for the quick response
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@FernandoTBarros make sure that you set the correct items in
CONTAINER_REGISTRIES
.