start on deployments

This commit is contained in:
mbecker20
2022-03-15 04:33:35 -07:00
parent 82371db1c3
commit a36d7ba912
8 changed files with 215 additions and 427 deletions
-357
View File
@@ -1,357 +0,0 @@
import { Build, User } from "@monitor/types";
import { buildChangelog, clone, dockerBuild, pull } from "@monitor/util";
import { FastifyInstance } from "fastify";
import { move, pathExists, remove } from "fs-extra";
import { WebSocket } from "ws";
import { PERMISSIONS_DENY_LOG, REGISTRY_URL, REPO_PATH } from "../config";
import { BUILDING, PULLING } from "../plugins/actionStates";
import { toDashedName } from "../util/helpers";
import { addBuildUpdate, addSystemUpdate } from "../util/updates";
const CREATE_BUILD = "CREATE_BUILD";
const DELETE_BUILD = "DELETE_BUILD";
const UPDATE_BUILD = "UPDATE_BUILD";
const PULL = "PULL";
const BUILD = "BUILD";
const CLONE_REPO = "CLONE_REPO";
async function buildMessages(
app: FastifyInstance,
client: WebSocket,
message: any,
user: User
) {
switch (message.type) {
case CREATE_BUILD:
const created = message.build && (await createBuild(app, user, message));
if (created) {
app.broadcast(CREATE_BUILD, { build: created });
}
return true;
case DELETE_BUILD:
const deleted =
message.buildID && (await deleteBuild(app, user, message));
if (deleted) {
app.broadcast(DELETE_BUILD, { buildID: message.buildID });
}
return true;
case UPDATE_BUILD:
const updated = message.build && (await updateBuild(app, user, message));
if (updated) {
app.broadcast(UPDATE_BUILD, { build: updated });
}
return true;
case PULL:
message.buildID && (await pullRepo(app, user, message));
return true;
case BUILD:
message.buildID && (await build(app, user, message));
return true;
default:
return false;
}
}
async function createBuild(
app: FastifyInstance,
user: User,
message: { build: Build; note?: string }
) {
if (user.permissions! >= 1) {
try {
const build = await app.builds.create({
...message.build,
imageName: toDashedName(message.build.name),
owner: user.username,
});
app.buildActionStates.add(build._id!);
addBuildUpdate(
app,
build._id!,
CREATE_BUILD,
"Create Build",
{ stdout: "Build Created: " + build.name },
user.username,
message.note
);
if (build.repo) {
await cloneRepo(app, user, build);
}
return build;
} catch (err) {
addSystemUpdate(
app,
CREATE_BUILD,
"Create Build (ERROR)",
{ stderr: JSON.stringify(err) },
user.username,
message.note,
true
);
}
} else {
addSystemUpdate(
app,
CREATE_BUILD,
"Create Build (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
message.note,
true
);
}
}
async function deleteBuild(
app: FastifyInstance,
user: User,
{ buildID, note }: { buildID: string; note?: string }
) {
const build = await app.builds.findById(buildID).catch(() => {});
if (!build) return;
if (user.permissions! >= 2 || user.username === build.owner) {
try {
await app.builds.findByIdAndDelete(buildID);
await app.deployments.updateMany(
{ buildID: build._id },
{ buildID: undefined }
);
if (build!.repo) await remove(REPO_PATH + build.imageName);
app.buildActionStates.delete(buildID);
addSystemUpdate(
app,
DELETE_BUILD,
"Delete Build",
{},
user.username,
note
);
return true;
} catch (error) {
addSystemUpdate(
app,
DELETE_BUILD,
"Delete Build",
{
stderr: JSON.stringify(error),
},
user.username,
note,
true
);
}
} else {
addSystemUpdate(
app,
DELETE_BUILD,
"Delete Build (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
note,
true
);
}
}
async function updateBuild(
app: FastifyInstance,
user: User,
{ build, note }: { build: Build; note?: string }
) {
const preBuild = await app.builds.findById(build._id!).catch(() => {});
if (!preBuild) return; // may want to add some update here
if (user.permissions! >= 2 || user.username === preBuild.owner) {
try {
build.imageName = toDashedName(build.name);
if (build.repo !== preBuild.repo || build.branch !== preBuild.branch) {
// reclone repo if repo is changed
await remove(REPO_PATH + preBuild.imageName).catch();
if (build.repo) {
await cloneRepo(app, user, build);
}
} else if (build.imageName !== preBuild.imageName) {
if (await pathExists(REPO_PATH + preBuild.imageName)) {
await move(
REPO_PATH + preBuild.imageName,
REPO_PATH + build.imageName
);
}
// maybe do something more with deployments
}
await app.builds.updateOne({ _id: build._id }, build);
addBuildUpdate(
app,
build._id!,
UPDATE_BUILD,
"Update Build",
{ stdout: buildChangelog(preBuild, build) },
user.username,
note
);
return build;
} catch (error) {
addBuildUpdate(
app,
build._id!,
UPDATE_BUILD,
"Update Build (ERROR)",
{
stderr: JSON.stringify(error),
},
user.username,
note,
true
);
}
} else {
addSystemUpdate(
app,
UPDATE_BUILD,
"Update Build (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
note,
true
);
}
}
async function cloneRepo(
app: FastifyInstance,
user: User,
{ imageName, branch, repo, accessToken, _id }: Build
) {
const { command, log, isError } = await clone(
repo!,
imageName!,
branch,
accessToken
);
addBuildUpdate(
app,
_id!,
CLONE_REPO,
command,
log,
user.username,
"",
!isError
);
}
async function pullRepo(
app: FastifyInstance,
user: User,
{ buildID, note }: { buildID: string; note?: string }
) {
const build = await app.builds.findById(buildID);
if (!build) return;
if (user.permissions! >= 2 || build.owner === user.username) {
if (!app.buildActionStates.get(buildID, PULLING)) {
app.buildActionStates.set(buildID, PULLING, true);
app.broadcast(PULL, { complete: false, buildID });
try {
const { imageName, branch } = build;
const { command, log, isError } = await pull(
REPO_PATH + imageName,
branch
);
addBuildUpdate(
app,
buildID,
PULL,
command,
log,
user.username,
note,
isError
);
} catch (error) {
addBuildUpdate(
app,
buildID,
PULL,
"Pull (ERROR)",
{ stderr: JSON.stringify(error) },
user.username,
note,
true
);
}
app.broadcast(PULL, { complete: true, buildID });
app.buildActionStates.set(buildID, PULLING, false);
}
} else {
addBuildUpdate(
app,
buildID,
PULL,
"Pull (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
note,
true
);
}
}
async function build(
app: FastifyInstance,
user: User,
{ buildID, note }: { buildID: string; note?: string }
) {
const build = await app.builds.findById(buildID);
if (!build) return;
if (user.permissions! >= 2 || build.owner === user.username) {
app.buildActionStates.set(buildID, BUILDING, true);
app.broadcast(BUILD, { complete: false, buildID });
try {
const { command, log, isError } = await dockerBuild(
build,
REPO_PATH,
REGISTRY_URL
);
addBuildUpdate(
app,
buildID,
BUILD,
command,
log,
user.username,
note,
isError
);
} catch (error) {
addBuildUpdate(
app,
buildID,
BUILD,
"Build (ERROR)",
{ stderr: JSON.stringify(error) },
user.username,
note,
true
);
}
app.broadcast(BUILD, { complete: true, buildID });
app.buildActionStates.set(buildID, BUILDING, false);
} else {
addBuildUpdate(
app,
buildID,
BUILD,
"Build (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
note,
true
);
}
}
export default buildMessages;
-4
View File
@@ -1,10 +1,6 @@
import { User } from "@monitor/types";
import { dockerBuild, pull } from "@monitor/util";
import { FastifyInstance } from "fastify";
import { WebSocket } from "ws";
import { PERMISSIONS_DENY_LOG, REGISTRY_URL, REPO_PATH } from "../../config";
import { BUILDING, PULLING } from "../../plugins/actionStates";
import { addBuildUpdate, addSystemUpdate } from "../../util/updates";
import build from "./build";
import createBuild from "./create";
import deleteBuild from "./delete";
-64
View File
@@ -1,64 +0,0 @@
import { Action, User } from "@monitor/types";
import { FastifyInstance } from "fastify";
import { WebSocket } from "ws";
const CREATE_DEPLOYMENT = "CREATE_DEPLOYMENT";
const DELETE_DEPLOYMENT = "DELETE_DEPLOYMENT";
const UPDATE_DEPLOYMENT = "UPDATE_DEPLOYMENT";
const DEPLOY = "DEPLOY";
const REDEPLOY = "REDEPLOY";
const START_CONTAINER = "START_CONTAINER";
const STOP_CONTAINER = "STOP_CONTAINER";
const DELETE_CONTAINER = "DELETE_CONTAINER";
const REFRESH_CONTAINER_STATUS = "REFRESH_CONTAINER_STATUS";
const COPY_ENV = "COPY_ENV";
const DEPLOY_TIMEOUT = 1000;
const DEPLOY_RECHECK_TIMEOUT = 3000;
async function deploymentMessages(
app: FastifyInstance,
client: WebSocket,
message: Action & object,
user: User
) {
switch (message.type) {
case CREATE_DEPLOYMENT:
return true;
case DELETE_DEPLOYMENT:
return true;
case UPDATE_DEPLOYMENT:
return true;
case DEPLOY:
return true;
case REDEPLOY:
return true;
case START_CONTAINER:
return true;
case STOP_CONTAINER:
return true;
case UPDATE_DEPLOYMENT:
return true;
case DELETE_CONTAINER:
return true;
case REFRESH_CONTAINER_STATUS:
return true;
case COPY_ENV:
return true;
default:
return false;
}
}
export default deploymentMessages;
+41
View File
@@ -0,0 +1,41 @@
import { Deployment, User } from "@monitor/types";
import { FastifyInstance } from "fastify";
import { CREATE_DEPLOYMENT } from ".";
import { PERMISSIONS_DENY_LOG } from "../../config";
import { toDashedName } from "../../util/helpers";
import { addDeploymentUpdate, addSystemUpdate } from "../../util/updates";
async function createDeployment(
app: FastifyInstance,
user: User,
{ deployment }: { deployment: Deployment }
) {
if (user.permissions! >= 1) {
const created = await app.deployments.create({
...deployment,
containerName: toDashedName(deployment.name),
owner: user.username,
});
app.deployActionStates.add(created._id!);
addDeploymentUpdate(
app,
created._id!,
CREATE_DEPLOYMENT,
"Create Deployment",
{ stdout: "Deployment Created: " + deployment.name },
user.username
);
} else {
addSystemUpdate(
app,
CREATE_DEPLOYMENT,
"Create Deployment (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
"",
true
);
}
}
export default createDeployment;
+76
View File
@@ -0,0 +1,76 @@
import { User } from "@monitor/types";
import { FastifyInstance } from "fastify";
import { DELETE_DEPLOYMENT } from ".";
import { PERMISSIONS_DENY_LOG } from "../../config";
import { addDeploymentUpdate, addSystemUpdate } from "../../util/updates";
const deploymentViewFields = [
"name",
"image",
"ports",
"volumes",
"environment",
"network",
"logToAWS",
"useServerRoot",
"restart",
"autoDeploy",
]; // the fields shown in the update log
async function deleteDeployment(
app: FastifyInstance,
user: User,
{ deploymentID, note }: { deploymentID: string; note?: string }
) {
const deployment = await app.deployments.findById(deploymentID);
if (!deployment) return;
if (user.permissions! >= 2 || user.username === deployment.owner) {
try {
const stopLog = (deployment.image || deployment.buildID) && ""; // stop container, either locally or remote
if (deployment.image || deployment.buildID) {
// stop container, either locally or remote
}
addSystemUpdate(
app,
DELETE_DEPLOYMENT,
"Delete Deployment",
{
stdout:
"Removed:\n\n" +
deploymentViewFields
.map((field) => {
return `${field}: ${JSON.stringify(deployment[field])}\n`;
})
.reduce((prev, curr) => prev + curr) +
(stopLog && `\n\nDocker Stop Log:\n\n${JSON.stringify(stopLog)}`),
},
user.username,
note
);
return true;
} catch (error) {
addSystemUpdate(
app,
DELETE_DEPLOYMENT,
"Delete Deployment (ERROR)",
{ stderr: JSON.stringify(error) },
user.username,
note,
true
);
}
} else {
addDeploymentUpdate(
app,
deploymentID,
DELETE_DEPLOYMENT,
"Delete Deployment (DENIED)",
PERMISSIONS_DENY_LOG,
user.username,
note,
true
);
}
}
export default deleteDeployment;
+78
View File
@@ -0,0 +1,78 @@
import { Action, User } from "@monitor/types";
import { FastifyInstance } from "fastify";
import { WebSocket } from "ws";
import createDeployment from "./create";
import deleteDeployment from "./delete";
export const CREATE_DEPLOYMENT = "CREATE_DEPLOYMENT";
export const DELETE_DEPLOYMENT = "DELETE_DEPLOYMENT";
export const UPDATE_DEPLOYMENT = "UPDATE_DEPLOYMENT";
export const DEPLOY = "DEPLOY";
export const REDEPLOY = "REDEPLOY";
export const START_CONTAINER = "START_CONTAINER";
export const STOP_CONTAINER = "STOP_CONTAINER";
export const DELETE_CONTAINER = "DELETE_CONTAINER";
export const REFRESH_CONTAINER_STATUS = "REFRESH_CONTAINER_STATUS";
export const COPY_ENV = "COPY_ENV";
const DEPLOY_TIMEOUT = 1000;
const DEPLOY_RECHECK_TIMEOUT = 3000;
async function deploymentMessages(
app: FastifyInstance,
client: WebSocket,
message: any,
user: User
) {
switch (message.type) {
case CREATE_DEPLOYMENT:
const created =
message.deployment && (await createDeployment(app, user, message));
if (created) {
app.broadcast(CREATE_DEPLOYMENT, { deployment: created });
}
return true;
case DELETE_DEPLOYMENT:
const deleted =
message.deploymentID && (await deleteDeployment(app, user, message));
if (deleted) {
app.broadcast(DELETE_DEPLOYMENT, {
deploymentID: message.deploymentID,
});
}
return true;
case UPDATE_DEPLOYMENT:
return true;
case DEPLOY:
return true;
case REDEPLOY:
return true;
case START_CONTAINER:
return true;
case STOP_CONTAINER:
return true;
case UPDATE_DEPLOYMENT:
return true;
case DELETE_CONTAINER:
return true;
case REFRESH_CONTAINER_STATUS:
return true;
case COPY_ENV:
return true;
default:
return false;
}
}
export default deploymentMessages;
+2 -2
View File
@@ -1,8 +1,8 @@
import { Action, User } from "@monitor/types";
import { FastifyInstance } from "fastify";
import { WebSocket } from "ws";
import buildMessages from "./build";
import deploymentMessages from "./deployment";
import buildMessages from "./builds";
import deploymentMessages from "./deployments";
import serverMessages from "./server";
export default async function handleMessage(
+18
View File
@@ -7,6 +7,24 @@ import {
} from "@monitor/types";
import { execute } from "./execute";
/* Container */
export async function startContainer(containerName: string) {
const command = `docker start ${containerName}`;
return {
command,
...(await execute(command)),
}
}
export async function deleteContainer(containerName: string) {
const command = `docker stop ${containerName} && docker container rm ${containerName}`;
return {
command,
...(await execute(command)),
}
}
/* Docker Build */
export async function dockerBuild(