Skip to content

Commit

Permalink
Add discovery search for projects within stacks directory that are not
Browse files Browse the repository at this point in the history
known to docker compose
  • Loading branch information
mkoo21 committed Dec 14, 2024
1 parent 5115032 commit 6a619bf
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 3 deletions.
60 changes: 57 additions & 3 deletions backend/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { DockgeSocket, fileExists, ValidationError } from "./util-server";
import path from "path";
import {
acceptedComposeFileNames,
acceptedComposeFileNamePattern,
ArbitrarilyNestedLooseObject,
COMBINED_TERMINAL_COLS,
COMBINED_TERMINAL_ROWS,
CREATED_FILE,
Expand Down Expand Up @@ -271,7 +273,7 @@ export class Stack {
return stackList;
}

// Get status from docker compose ls
// Get stacks from docker compose ls
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], {
encoding: "utf-8",
});
Expand All @@ -282,6 +284,7 @@ export class Stack {
}

let composeList = JSON.parse(res.stdout.toString());
let pathSearchTree: ArbitrarilyNestedLooseObject = {}; // search structure for matching paths

for (let composeStack of composeList) {
try {
Expand All @@ -292,11 +295,63 @@ export class Stack {
stack._configFilePath = path.dirname(composeFiles[0]);
stack._composeFileName = path.basename(composeFiles[0]);
stackList.set(composeStack.Name, stack);

// add project path to search structure to use later
// e.g. path "/opt/stacks" would yield the tree { opt: stacks: {} }
path.join(stack._configFilePath, stack._composeFileName).split(path.sep).reduce((searchTree, pathComponent) => {
if (pathComponent == "") {
return searchTree;
}
if (!searchTree[pathComponent]) {
searchTree[pathComponent] = {};
}
return searchTree;
}, pathSearchTree);
} catch (e) {
if (e instanceof Error) {
log.warn("getStackList", `Failed to get stack ${composeStack.Name}, error: ${e.message}`);
log.error("getStackList", `Failed to get stack ${composeStack.Name}, error: ${e.message}`);
}
}
}

// Search stacks directory for compose files not associated with a running compose project (ie. never started through CLI)
try {
// Hopefully the user has access to everything in this directory! If they don't, log the error. It is a small price to pay for fast searching.
let rawFilesList = fs.readdirSync(server.stacksDir, {
recursive: true,
withFileTypes: true
});
let acceptedComposeFiles = rawFilesList.filter((dirEnt: fs.Dirent) => dirEnt.isFile() && !!dirEnt.name.match(acceptedComposeFileNamePattern));
for (let composeFile of acceptedComposeFiles) {
// check if we have seen this file before
let fullPath = path.join(server.stacksDir, composeFile.parentPath);
let previouslySeen = fullPath.split(path.sep).reduce((searchTree: ArbitrarilyNestedLooseObject | boolean, pathComponent) => {
if (pathComponent == "") {
return searchTree;
}

// end condition
if (searchTree == false || !(searchTree as ArbitrarilyNestedLooseObject)[pathComponent]) {
return false;
}

// path (so far) has been previously seen
return (searchTree as ArbitrarilyNestedLooseObject)[pathComponent];
}, pathSearchTree);
if (!previouslySeen) {
// a file with an accepted compose filename has been found that did not appear in `docker compose ls`. Use its config file path as a temp name
let [ configFilePath, configFilename ] = [ path.dirname(fullPath), path.basename(fullPath) ];
let stack = new Stack(server, configFilePath);
stack._status = UNKNOWN;
stack._configFilePath = configFilePath;
stack._composeFileName = configFilename;
stackList.set(configFilePath, stack);
}
}
} catch (e) {
if (e instanceof Error) {
log.error("getStackList", `Got error searching for undiscovered stacks:\n${e.message}`);
}
}

this.managedStackList = stackList;
Expand Down Expand Up @@ -483,6 +538,5 @@ export class Stack {
log.error("getServiceStatusList", e);
return statusList;
}

}
}
11 changes: 11 additions & 0 deletions common/util-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export interface LooseObject {
[key: string]: any
}

export interface ArbitrarilyNestedLooseObject {
[key: string]: ArbitrarilyNestedLooseObject | Record<string, never>;
}

export interface BaseRes {
ok: boolean;
msg?: string;
Expand Down Expand Up @@ -125,6 +129,13 @@ export const acceptedComposeFileNames = [
"compose.yml",
];

// Make a regex out of accepted compose file names
export const acceptedComposeFileNamePattern = new RegExp(
acceptedComposeFileNames
.map((filename: string) => filename.replace(".", "\\$&"))
.join("|")
);

/**
* Generate a decimal integer number from a string
* @param str Input
Expand Down

0 comments on commit 6a619bf

Please sign in to comment.