Files
patchmon.net/backend/src/services/automation/dockerInventoryCleanup.js
2025-10-28 16:06:36 +00:00

165 lines
4.0 KiB
JavaScript

const { prisma } = require("./shared/prisma");
/**
* Docker Inventory Cleanup Automation
* Removes Docker containers and images for hosts that no longer exist
*/
class DockerInventoryCleanup {
constructor(queueManager) {
this.queueManager = queueManager;
this.queueName = "docker-inventory-cleanup";
}
/**
* Process Docker inventory cleanup job
*/
async process(_job) {
const startTime = Date.now();
console.log("🧹 Starting Docker inventory cleanup...");
try {
// Step 1: Find and delete orphaned containers (containers for non-existent hosts)
const orphanedContainers = await prisma.docker_containers.findMany({
where: {
host_id: {
// Find containers where the host doesn't exist
notIn: await prisma.hosts
.findMany({ select: { id: true } })
.then((hosts) => hosts.map((h) => h.id)),
},
},
});
let deletedContainersCount = 0;
const deletedContainers = [];
for (const container of orphanedContainers) {
try {
await prisma.docker_containers.delete({
where: { id: container.id },
});
deletedContainersCount++;
deletedContainers.push({
id: container.id,
container_id: container.container_id,
name: container.name,
image_name: container.image_name,
host_id: container.host_id,
});
console.log(
`🗑️ Deleted orphaned container: ${container.name} (host_id: ${container.host_id})`,
);
} catch (deleteError) {
console.error(
`❌ Failed to delete container ${container.id}:`,
deleteError.message,
);
}
}
// Step 2: Find and delete orphaned images (images with no containers using them)
const orphanedImages = await prisma.docker_images.findMany({
where: {
docker_containers: {
none: {},
},
},
include: {
_count: {
select: {
docker_containers: true,
docker_image_updates: true,
},
},
},
});
let deletedImagesCount = 0;
const deletedImages = [];
for (const image of orphanedImages) {
try {
// First delete any image updates associated with this image
if (image._count.docker_image_updates > 0) {
await prisma.docker_image_updates.deleteMany({
where: { image_id: image.id },
});
}
// Then delete the image itself
await prisma.docker_images.delete({
where: { id: image.id },
});
deletedImagesCount++;
deletedImages.push({
id: image.id,
repository: image.repository,
tag: image.tag,
image_id: image.image_id,
});
console.log(
`🗑️ Deleted orphaned image: ${image.repository}:${image.tag}`,
);
} catch (deleteError) {
console.error(
`❌ Failed to delete image ${image.id}:`,
deleteError.message,
);
}
}
const executionTime = Date.now() - startTime;
console.log(
`✅ Docker inventory cleanup completed in ${executionTime}ms - Deleted ${deletedContainersCount} containers and ${deletedImagesCount} images`,
);
return {
success: true,
deletedContainersCount,
deletedImagesCount,
deletedContainers,
deletedImages,
executionTime,
};
} catch (error) {
const executionTime = Date.now() - startTime;
console.error(
`❌ Docker inventory cleanup failed after ${executionTime}ms:`,
error.message,
);
throw error;
}
}
/**
* Schedule recurring Docker inventory cleanup (daily at 4 AM)
*/
async schedule() {
const job = await this.queueManager.queues[this.queueName].add(
"docker-inventory-cleanup",
{},
{
repeat: { cron: "0 4 * * *" }, // Daily at 4 AM
jobId: "docker-inventory-cleanup-recurring",
},
);
console.log("✅ Docker inventory cleanup scheduled");
return job;
}
/**
* Trigger manual Docker inventory cleanup
*/
async triggerManual() {
const job = await this.queueManager.queues[this.queueName].add(
"docker-inventory-cleanup-manual",
{},
{ priority: 1 },
);
console.log("✅ Manual Docker inventory cleanup triggered");
return job;
}
}
module.exports = DockerInventoryCleanup;