diff --git a/src/components/services/status.jsx b/src/components/services/status.jsx index 81d485891..afe161fbf 100644 --- a/src/components/services/status.jsx +++ b/src/components/services/status.jsx @@ -12,7 +12,7 @@ export default function Status({ service }) { } - if (data && data.status === "running") { + if (data && data.status.includes("running")) { if (data.health === "starting") { return (
@@ -36,7 +36,7 @@ export default function Status({ service }) { ); } - if (data && (data.status === "not found" || data.status === "exited")) { + if (data && (data.status === "not found" || data.status === "exited" || data.status?.startsWith("partial"))) { return (
{data.status}
diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js index 84e90065f..0ca30f001 100644 --- a/src/pages/api/docker/stats/[...service].js +++ b/src/pages/api/docker/stats/[...service].js @@ -53,17 +53,25 @@ export default async function handler(req, res) { }) .catch(() => []); - // For now we are only interested in the first one (in case replicas > 1). // TODO: Show the result for all replicas/containers? - const taskContainerId = tasks.at(0)?.Status?.ContainerStatus?.ContainerID; - + // We can only get stats for 'local' containers so try to find one + const localContainerIDs = containers.map(c => c.Id); + const task = tasks.find(t => localContainerIDs.includes(t.Status?.ContainerStatus?.ContainerID)) ?? tasks.at(0); + const taskContainerId = task?.Status?.ContainerStatus?.ContainerID; + if (taskContainerId) { - const container = docker.getContainer(taskContainerId); - const stats = await container.stats({ stream: false }); + try { + const container = docker.getContainer(taskContainerId); + const stats = await container.stats({ stream: false }); - return res.status(200).json({ - stats, - }); + return res.status(200).json({ + stats, + }); + } catch (e) { + return res.status(200).json({ + error: "Unable to retrieve stats" + }) + } } } diff --git a/src/pages/api/docker/status/[...service].js b/src/pages/api/docker/status/[...service].js index f232eb98e..2bcc0a8a6 100644 --- a/src/pages/api/docker/status/[...service].js +++ b/src/pages/api/docker/status/[...service].js @@ -44,32 +44,65 @@ export default async function handler(req, res) { } if (dockerArgs.swarm) { - const tasks = await docker.listTasks({ + const serviceInfo = await docker.getService(containerName).inspect() + .catch(() => undefined); + + if (!serviceInfo) { + return res.status(404).send({ + status: "not found", + }); + } + + const tasks = await docker + .listTasks({ filters: { service: [containerName], - // A service can have several offline containers, we only look for an active one. "desired-state": ["running"], }, }) .catch(() => []); - // For now we are only interested in the first one (in case replicas > 1). - // TODO: Show the result for all replicas/containers? - const taskContainerId = tasks.at(0)?.Status?.ContainerStatus?.ContainerID; - - if (taskContainerId) { - const container = docker.getContainer(taskContainerId); - const info = await container.inspect(); - - return res.status(200).json({ - status: info.State.Status, - health: info.State.Health?.Status, - }); + if (serviceInfo.Spec.Mode?.Replicated) { + // Replicated service, check n replicas + const replicas = parseInt(serviceInfo.Spec.Mode?.Replicated?.Replicas, 10); + if (tasks.length === replicas) { + return res.status(200).json({ + status: `running ${tasks.length}/${replicas}`, + }); + } + if (tasks.length > 0) { + return res.status(200).json({ + status: `partial ${tasks.length}/${replicas}`, + }); + } + } else { + // Global service, prefer 'local' containers + const localContainerIDs = containers.map(c => c.Id); + const task = tasks.find(t => localContainerIDs.includes(t.Status?.ContainerStatus?.ContainerID)) ?? tasks.at(0); + const taskContainerId = task?.Status?.ContainerStatus?.ContainerID; + + if (taskContainerId) { + try { + const container = docker.getContainer(taskContainerId); + const info = await container.inspect(); + + return res.status(200).json({ + status: info.State.Status, + health: info.State.Health?.Status, + }); + } catch (e) { + if (task) { + return res.status(200).json({ + status: task.Status.State + }) + } + } + } } } - return res.status(200).send({ - error: "not found", + return res.status(404).send({ + status: "not found", }); } catch (e) { logger.error(e); diff --git a/src/widgets/docker/component.jsx b/src/widgets/docker/component.jsx index 9d3affd3d..7ef5e6cec 100644 --- a/src/widgets/docker/component.jsx +++ b/src/widgets/docker/component.jsx @@ -22,7 +22,7 @@ export default function Component({ service }) { return ; } - if (statusData && statusData.status !== "running") { + if (statusData && !(statusData.status.includes("running") || statusData.status.includes("partial"))) { return (