feat: display containers using the volume

This commit is contained in:
Nicolas Meienberger
2025-09-27 10:16:23 +02:00
parent 481eb13a44
commit 592a8f52b7
10 changed files with 353 additions and 45 deletions

View File

@@ -1,8 +1,8 @@
import { and, eq, or } from "drizzle-orm";
import { getTasks, schedule } from "node-cron";
import { db } from "../../db/db";
import { logger } from "../../utils/logger";
import { volumesTable } from "../../db/schema";
import { schedule, getTasks } from "node-cron";
import { logger } from "../../utils/logger";
import { volumeService } from "../volumes/volume.service";
export const startup = async () => {
@@ -18,7 +18,7 @@ export const startup = async () => {
}
const existingTasks = getTasks();
existingTasks.forEach((task) => task.destroy());
existingTasks.forEach(async (task) => await task.destroy());
schedule("* * * * *", async () => {
logger.info("Running health check for all volumes...");

View File

@@ -4,16 +4,18 @@ import {
createVolumeBody,
createVolumeDto,
deleteVolumeDto,
type GetVolumeResponseDto,
getContainersDto,
getVolumeDto,
type ListContainersResponseDto,
type ListVolumesResponseDto,
listVolumesDto,
mountVolumeDto,
testConnectionBody,
testConnectionDto,
unmountVolumeDto,
updateVolumeBody,
updateVolumeDto,
mountVolumeDto,
unmountVolumeDto,
type GetVolumeResponseDto,
} from "./volume.dto";
import { volumeService } from "./volume.service";
@@ -70,6 +72,16 @@ export const volumeController = new Hono()
return c.json(response, 200);
})
.get("/:name/containers", getContainersDto, async (c) => {
const { name } = c.req.param();
const { containers } = await volumeService.getContainersUsingVolume(name);
const response = {
containers,
} satisfies ListContainersResponseDto;
return c.json(response, 200);
})
.put("/:name", updateVolumeDto, validator("json", updateVolumeBody), async (c) => {
const { name } = c.req.param();
const body = c.req.valid("json");

View File

@@ -258,3 +258,38 @@ export const unmountVolumeDto = describeRoute({
},
},
});
/**
* Get containers using a volume
*/
const containerSchema = type({
id: "string",
name: "string",
state: "string",
image: "string",
});
export const listContainersResponse = type({
containers: containerSchema.array(),
});
export type ListContainersResponseDto = typeof listContainersResponse.infer;
export const getContainersDto = describeRoute({
description: "Get containers using a volume by name",
operationId: "getContainersUsingVolume",
validateResponse: true,
tags: ["Volumes"],
responses: {
200: {
description: "List of containers using the volume",
content: {
"application/json": {
schema: resolver(listContainersResponse),
},
},
},
404: {
description: "Volume not found",
},
},
});

View File

@@ -2,17 +2,17 @@ import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import type { BackendConfig } from "@ironmount/schemas";
import Docker from "dockerode";
import { eq } from "drizzle-orm";
import { ConflictError, InternalServerError, NotFoundError } from "http-errors-enhanced";
import slugify from "slugify";
import { config } from "../../core/config";
import { VOLUME_MOUNT_BASE } from "../../core/constants";
import { db } from "../../db/db";
import { volumesTable } from "../../db/schema";
import { createVolumeBackend } from "../backends/backend";
import { toMessage } from "../../utils/errors";
import { getStatFs, type StatFs } from "../../utils/mountinfo";
import { VOLUME_MOUNT_BASE } from "../../core/constants";
import { logger } from "../../utils/logger";
import { createVolumeBackend } from "../backends/backend";
const listVolumes = async () => {
const volumes = await db.query.volumesTable.findMany({});
@@ -192,6 +192,37 @@ const checkHealth = async (name: string) => {
return { status, error };
};
const getContainersUsingVolume = async (name: string) => {
const volume = await db.query.volumesTable.findFirst({
where: eq(volumesTable.name, name),
});
if (!volume) {
throw new NotFoundError("Volume not found");
}
const docker = new Docker();
const containers = await docker.listContainers({ all: true });
const usingContainers = [];
for (const info of containers) {
const container = docker.getContainer(info.Id);
const inspect = await container.inspect();
const mounts = inspect.Mounts || [];
const usesVolume = mounts.some((mount) => mount.Type === "volume" && mount.Name === name);
if (usesVolume) {
usingContainers.push({
id: inspect.Id,
name: inspect.Name,
state: inspect.State.Status,
image: inspect.Config.Image,
});
}
}
return { containers: usingContainers };
};
export const volumeService = {
listVolumes,
createVolume,
@@ -202,4 +233,5 @@ export const volumeService = {
testConnection,
unmountVolume,
checkHealth,
getContainersUsingVolume,
};