Skip to content

Instantly share code, notes, and snippets.

@krasimir
Last active May 25, 2024 00:30
Show Gist options
  • Save krasimir/8dd6f608fc829e5e5aa9ab0136452706 to your computer and use it in GitHub Desktop.
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)
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]);
}
})();
@krasimir
Copy link
Author

@C4sse you have to run

> node delete_artifacts.js

@georgenhari
Copy link

georgenhari commented Oct 13, 2021

thank you very much
i just have a question, i dont understand much of js
i have 4 buckets i would like to clear us.artifacts and keep none of the builds (const KEEP_AT_LEAST = 0;)
i have 1 storage bucket with my users images that they post i would like to keep that one untouched.
Another thing i dont need any extra set up? just inserting my project name?

@krasimir
Copy link
Author

As is the script written you have to amend the CONTAINER_REGISTRIES array and add what you want to clear there. At the moment KEEP_AT_LEAST constant is used for every registry so you have to change the script if you need to have different number for each item.

@FernandoTBarros
Copy link

I have 1GB on my Container Registry, but the code didn't remove or show nothing to remove. Why is that?

@krasimir
Copy link
Author

@FernandoTBarros make sure that you set the correct items in CONTAINER_REGISTRIES.

@Jmaiers
Copy link

Jmaiers commented May 26, 2022

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?

@krasimir
Copy link
Author

@Jmaiers yep, you are right. I guess I wanted to have an insurance in case of latest is messed up.

@Jmaiers
Copy link

Jmaiers commented May 26, 2022

@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