add prune containers functionality

This commit is contained in:
beckerinj
2022-08-09 01:46:05 -04:00
parent d1b1fa40e6
commit 7b1c01f402
10 changed files with 146 additions and 10 deletions
+21 -4
View File
@@ -4,6 +4,7 @@ import {
CREATE_NETWORK,
DELETE_NETWORK,
GET_SERVER_STATS,
PRUNE_CONTAINERS,
PRUNE_IMAGES,
PRUNE_NETWORKS,
REMOVE_SERVER,
@@ -13,7 +14,7 @@ import { FastifyInstance } from "fastify";
import { WebSocket } from "ws";
import addServer from "./add";
import { createServerNetwork, deleteServerNetwork } from "./networks";
import { pruneServerImages, pruneServerNetworks } from "./prune";
import { pruneServerContainers, pruneServerImages, pruneServerNetworks } from "./prune";
import removeServer from "./remove";
import updateServer from "./update";
@@ -27,7 +28,11 @@ async function serverMessages(
case ADD_SERVER:
const created = message.server && (await addServer(app, user, message));
if (created) {
app.broadcast(ADD_SERVER, { server: created }, app.serverUserFilter(created._id));
app.broadcast(
ADD_SERVER,
{ server: created },
app.serverUserFilter(created._id)
);
}
return true;
@@ -35,7 +40,11 @@ async function serverMessages(
const removed =
message.serverID && (await removeServer(app, user, message));
if (removed) {
app.broadcast(REMOVE_SERVER, { serverID: message.serverID }, app.serverUserFilter(removed._id, removed));
app.broadcast(
REMOVE_SERVER,
{ serverID: message.serverID },
app.serverUserFilter(removed._id, removed)
);
}
return true;
@@ -43,7 +52,11 @@ async function serverMessages(
const updated =
message.server && (await updateServer(app, user, message));
if (updated) {
app.broadcast(UPDATE_SERVER, { server: updated }, app.serverUserFilter(updated._id));
app.broadcast(
UPDATE_SERVER,
{ server: updated },
app.serverUserFilter(updated._id)
);
}
return true;
@@ -51,6 +64,10 @@ async function serverMessages(
await pruneServerImages(app, client, user, message);
return true;
case PRUNE_CONTAINERS:
await pruneServerContainers(app, client, user, message);
return true;
case CREATE_NETWORK:
message.serverID &&
message.name &&
+55 -2
View File
@@ -1,15 +1,16 @@
import { User } from "@monitor/types";
import {
PRUNE_CONTAINERS,
PRUNE_IMAGES,
PRUNE_NETWORKS,
} from "@monitor/util";
import { pruneImages, pruneNetworks } from "@monitor/util-node";
import { pruneContainers, pruneImages, pruneNetworks } from "@monitor/util-node";
import { FastifyInstance } from "fastify";
import { WebSocket } from "ws";
import { PERMISSIONS_DENY_LOG } from "../../config";
import { sendAlert } from "../../util/helpers";
import { prunePeripheryNetworks } from "../../util/periphery/networks";
import { prunePeripheryImages } from "../../util/periphery/server";
import { prunePeripheryContainers, prunePeripheryImages } from "../../util/periphery/server";
import { addServerUpdate } from "../../util/updates";
export async function pruneServerImages(
@@ -60,6 +61,58 @@ export async function pruneServerImages(
);
}
export async function pruneServerContainers(
app: FastifyInstance,
client: WebSocket,
user: User,
{ serverID, note }: { serverID: string; note?: string }
) {
if (app.serverActionStates.busy(serverID)) {
sendAlert(client, "bad", "server busy, try again in a bit");
return;
}
if (user.permissions! < 2) {
addServerUpdate(
app,
serverID,
PRUNE_CONTAINERS,
"Prune Containers (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
note,
true
);
return;
}
const server = await app.servers.findById(serverID);
if (!server) return;
app.serverActionStates.set(serverID, "pruningContainers", true);
app.broadcast(
PRUNE_CONTAINERS,
{ complete: false, serverID },
app.serverUserFilter(serverID)
);
const { command, log, isError } = server.isCore
? await pruneContainers()
: await prunePeripheryContainers(server);
addServerUpdate(
app,
serverID,
PRUNE_CONTAINERS,
command,
log,
user.username,
note,
isError
);
app.serverActionStates.set(serverID, "pruningContainers", false);
app.broadcast(
PRUNE_CONTAINERS,
{ complete: true, serverID },
app.serverUserFilter(serverID)
);
}
export async function pruneServerNetworks(
app: FastifyInstance,
client: WebSocket,
+2 -1
View File
@@ -138,6 +138,7 @@ const actionStates = fp((app: FastifyInstance, _: {}, done: () => void) => {
serverActionStates[serverID] = {
pruningImages: false,
pruningNetworks: false,
pruningContainers: false,
deleting: false,
};
},
@@ -157,7 +158,7 @@ const actionStates = fp((app: FastifyInstance, _: {}, done: () => void) => {
return false;
},
busy: (serverID: string) => {
for (const type of ["pruningImages", "pruningNetworks", "deleting"]) {
for (const type of ["pruningImages", "pruningNetworks", "pruningContainers", "deleting"]) {
if (serverActionStates[serverID][type]) return true;
}
return false;
+10
View File
@@ -12,6 +12,16 @@ export async function prunePeripheryImages({ address, passkey }: Server) {
.then(({ data }) => data);
}
export async function prunePeripheryContainers({ address, passkey }: Server) {
return await axios
.get<CommandLogError>(`${address}/containers/prune`, {
headers: {
Authorization: passkey || SECRETS.PASSKEY,
},
})
.then(({ data }) => data);
}
export async function getPeripheryDockerStats({ address, passkey }: Server) {
return await axios
.get<DockerStat[]>(`${address}/stats`, {
@@ -7,7 +7,7 @@ import {
useContext,
} from "solid-js";
import { createStore } from "solid-js/store";
import { PRUNE_IMAGES } from "@monitor/util";
import { PRUNE_CONTAINERS, PRUNE_IMAGES, PRUNE_NETWORKS } from "@monitor/util";
import { useAppState } from "../../state/StateProvider";
import { getServerActionState } from "../../util/query";
@@ -20,6 +20,7 @@ export const ActionStateProvider: Component<{}> = (p) => {
const [actions, setActions] = createStore<ServerActionState>({
pruningImages: false,
pruningNetworks: false,
pruningContainers: false,
deleting: false,
});
createEffect(() => {
@@ -32,6 +33,20 @@ export const ActionStateProvider: Component<{}> = (p) => {
}
})
);
onCleanup(
ws.subscribe([PRUNE_NETWORKS], ({ complete, serverID }) => {
if (serverID === selected.id()) {
setActions("pruningNetworks", !complete);
}
})
);
onCleanup(
ws.subscribe([PRUNE_CONTAINERS], ({ complete, serverID }) => {
if (serverID === selected.id()) {
setActions("pruningContainers", !complete);
}
})
);
return <context.Provider value={actions}>{p.children}</context.Provider>;
};
+30 -1
View File
@@ -1,6 +1,6 @@
import { Component, Show } from "solid-js";
import { pushNotification } from "../..";
import { PRUNE_IMAGES, PRUNE_NETWORKS } from "@monitor/util";
import { PRUNE_CONTAINERS, PRUNE_IMAGES, PRUNE_NETWORKS } from "@monitor/util";
import { useAppState } from "../../state/StateProvider";
import { useUser } from "../../state/UserProvider";
import ConfirmButton from "../util/ConfirmButton";
@@ -24,6 +24,9 @@ const Actions: Component<{}> = (p) => {
<Flex class={combineClasses("action shadow", themeClass())}>
prune images <PruneImages />
</Flex>
<Flex class={combineClasses("action shadow", themeClass())}>
prune containers <PruneContainers />
</Flex>
<Flex class={combineClasses("action shadow", themeClass())}>
prune networks{" "}
<ConfirmButton
@@ -68,3 +71,29 @@ function PruneImages() {
</Show>
);
}
function PruneContainers() {
const { ws, servers, selected } = useAppState();
const server = () => servers.get(selected.id())!;
const actions = useActionStates();
return (
<Show
when={!actions.pruningContainers}
fallback={
<button class="blue">
<Loading type="spinner" />
</button>
}
>
<ConfirmButton
color="blue"
onConfirm={() => {
ws.send(PRUNE_CONTAINERS, { serverID: server()._id });
pushNotification("ok", `pruning containers on ${server().name}...`);
}}
>
<Icon type="cut" />
</ConfirmButton>
</Show>
);
}
+6 -1
View File
@@ -1,4 +1,4 @@
import { pruneImages, getSystemStats, getDockerStatsJson } from "@monitor/util-node";
import { pruneImages, getSystemStats, getDockerStatsJson, pruneContainers } from "@monitor/util-node";
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
@@ -8,6 +8,11 @@ const server = fp((app: FastifyInstance, _: {}, done: () => void) => {
res.send(log);
});
app.get("/containers/prune", { onRequest: [app.auth] }, async (req, res) => {
const log = await pruneContainers();
res.send(log);
});
app.get("/stats", { onRequest: [app.auth] }, async (req, res) => {
const stats = await getDockerStatsJson();
res.send(stats);
+1
View File
@@ -182,6 +182,7 @@ export type DeployActionState = {
export type ServerActionState = {
pruningImages: boolean;
pruningNetworks: boolean;
pruningContainers: boolean;
deleting: boolean;
};
+4
View File
@@ -31,6 +31,10 @@ export async function pruneImages() {
return await execute("docker image prune -a -f");
}
export async function pruneContainers() {
return await execute("docker container prune -f");
}
export async function getNetworks(dockerode: Dockerode): Promise<Network[]> {
const networks = await dockerode.listNetworks();
return networks.map(({ Name, Driver }) => ({
+1
View File
@@ -27,6 +27,7 @@ export const ADD_SERVER = "ADD_SERVER";
export const REMOVE_SERVER = "REMOVE_SERVER";
export const UPDATE_SERVER = "UPDATE_SERVER";
export const PRUNE_IMAGES = "PRUNE_IMAGES";
export const PRUNE_CONTAINERS = "PRUNE_CONTAINERS";
export const GET_SERVER_STATS = "GET_SERVER_STATS";
export const CREATE_NETWORK = "CREATE_NETWORK";
export const DELETE_NETWORK = "DELETE_NETWORK";