mirror of
https://github.com/louislam/dockge.git
synced 2025-04-04 23:42:20 +00:00
Add discovery search for projects within stacks directory that are not
known to docker compose
This commit is contained in:
parent
fc4ad7ff29
commit
2c29aea921
2 changed files with 73 additions and 2 deletions
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -296,13 +299,71 @@ export class Stack {
|
|||
continue;
|
||||
}
|
||||
stackList.set(composeStack.Name, stack);
|
||||
|
||||
// add project path to search tree so we can quickly decide if we have seen it before 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[pathComponent];
|
||||
}, 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));
|
||||
log.debug("getStackList", `Folder scan yielded ${acceptedComposeFiles.length} files`);
|
||||
for (let composeFile of acceptedComposeFiles) {
|
||||
// check if we have seen this file before
|
||||
let fullPath = 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
|
||||
log.info("getStackList", `Found project unknown to docker compose: ${fullPath}/${composeFile.name}`);
|
||||
let [ configFilePath, configFilename, inferredProjectName ] = [ fullPath, composeFile.name, path.basename(fullPath) ];
|
||||
if (stackList.get(inferredProjectName)) {
|
||||
log.info("getStackList", `... but it was ignored. A project named ${inferredProjectName} already exists`);
|
||||
} else {
|
||||
let stack = new Stack(server, inferredProjectName);
|
||||
stack._status = UNKNOWN;
|
||||
stack._configFilePath = configFilePath;
|
||||
stack._composeFileName = configFilename;
|
||||
stackList.set(inferredProjectName, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
log.error("getStackList", `Got error searching for undiscovered stacks:\n${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.managedStackList = stackList;
|
||||
return stackList;
|
||||
}
|
||||
|
@ -492,6 +553,5 @@ export class Stack {
|
|||
log.error("getServiceStatusList", e);
|
||||
return statusList;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue