diff --git a/backend/socket-handlers/docker-socket-handler.ts b/backend/socket-handlers/docker-socket-handler.ts index 047d8f7..b42fa47 100644 --- a/backend/socket-handlers/docker-socket-handler.ts +++ b/backend/socket-handlers/docker-socket-handler.ts @@ -189,6 +189,28 @@ export class DockerSocketHandler extends SocketHandler { } }); + // rolloutStack + socket.on("rolloutStack", async (stackName : unknown, callback) => { + try { + checkLogin(socket); + + if (typeof(stackName) !== "string") { + throw new ValidationError("Stack name must be a string"); + } + + const stack = Stack.getStack(server, stackName); + await stack.rollout(socket); + callback({ + ok: true, + msg: "Rolled out" + }); + server.sendStackList(); + stack.joinCombinedTerminal(socket); + } catch (e) { + callbackError(e, callback); + } + }); + // down stack socket.on("downStack", async (stackName : unknown, callback) => { try { diff --git a/backend/stack.ts b/backend/stack.ts index a9af442..8b6d66f 100644 --- a/backend/stack.ts +++ b/backend/stack.ts @@ -323,6 +323,21 @@ export class Stack { return exitCode; } + async rollout(socket: DockgeSocket) { + let terminalName = getComposeTerminalName(this.name); + let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "pull" ], this.path); + if (exitCode !== 0) { + throw new Error("Failed to pull, please check the terminal output for more information."); + } + + terminalName = getComposeTerminalName(this.name); + exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "rollout", this.name ], this.path); + if (exitCode !== 0) { + throw new Error("Failed to rollout, please check the terminal output for more information."); + } + return exitCode; + } + async stop(socket: DockgeSocket) : Promise { const terminalName = getComposeTerminalName(this.name); let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "stop" ], this.path); diff --git a/frontend/src/icon.ts b/frontend/src/icon.ts index 0599e6a..746c41c 100644 --- a/frontend/src/icon.ts +++ b/frontend/src/icon.ts @@ -54,6 +54,7 @@ import { faTerminal, faWarehouse, faHome, faRocket, faRotate, faCloudArrowDown, faArrowsRotate, + faPaintRoller, } from "@fortawesome/free-solid-svg-icons"; library.add( @@ -109,6 +110,7 @@ library.add( faRotate, faCloudArrowDown, faArrowsRotate, + faPaintRoller, ); export { FontAwesomeIcon }; diff --git a/frontend/src/lang/en.json b/frontend/src/lang/en.json index e63df08..dc5826b 100644 --- a/frontend/src/lang/en.json +++ b/frontend/src/lang/en.json @@ -20,6 +20,7 @@ "updateStack": "Update", "startStack": "Start", "downStack": "Stop & Down", + "rolloutStack": "Rollout Update (Zero Downtime)", "editStack": "Edit", "discardStack": "Discard", "saveStackDraft": "Save", diff --git a/frontend/src/pages/Compose.vue b/frontend/src/pages/Compose.vue index 88db8ce..e19cac4 100644 --- a/frontend/src/pages/Compose.vue +++ b/frontend/src/pages/Compose.vue @@ -41,11 +41,15 @@ {{ $t("stopStack") }} - - + + {{ $t("downStack") }} + + + {{ $t("rolloutStack") }} Beta + @@ -486,6 +490,15 @@ export default { }); }, + rolloutStack() { + this.processing = true; + + this.$root.getSocket().emit("rolloutStack", this.stack.name, (res) => { + this.processing = false; + this.$root.toastRes(res); + }); + }, + downStack() { this.processing = true;