Merge pull request #1010 from bbassie/feature/plugin-browser

Plugin management through web interface
This commit is contained in:
Bastiaan authored and GitHub committed 2026-10-27 21:32:51 +00:00
1 parent e36a76acc5
commit ffe82f81d8
48 files changed
+4275 -536

No files matched your search

+44
View File
@@ -9,6 +9,7 @@ import * as lib from "@clusterio/lib";
import Host, { _discoverInstances, HostRouter } from "@clusterio/host/dist/node/src/Host.js";
import Instance from "@clusterio/host/dist/node/src/Instance.js";
import { HostConnector } from "@clusterio/host/dist/node/host.js";
import { computePluginStatuses } from "../../packages/web_ui/src/util/plugin_status.ts";
import "../setup_logging.js";
describe("host/src/Host", function() {
@@ -285,6 +286,31 @@ describe("host/src/Host", function() {
);
});
});
describe(".handlePluginListRequest()", function() {
it("does not expect a plugin without a host or instance entrypoint to load", async function() {
lib.addPluginConfigFields([{ name: "controller_only", controllerEntrypoint: "controller.js" }]);
const packagePath = fileURLToPath(import.meta.resolve("@clusterio/host/package.json"));
const pluginInfos = [{
name: "controller_only", title: "Controller only", version: "1.0.0",
requirePath: path.dirname(packagePath), packagePath, controllerEntrypoint: "controller.js",
}];
// Listed the same before and after a restart, as the host never loads it
for (let restart = 0; restart < 2; restart++) {
const hostConfig = new lib.HostConfig("host");
const host = new Host(new HostConnector(hostConfig, pluginInfos), hostConfig, pluginInfos);
await host.loadPlugins();
const plugins = await host.handlePluginListRequest(new lib.PluginListRequest());
const plugin = plugins.find(candidate => candidate.name === "controller_only");
assert.equal(plugin.loaded, false);
assert.equal(plugin.noEntrypoint, true);
const [entry] = computePluginStatuses([
{ name: "host", isController: false, address: { hostId: 1 }, plugins: [plugin] },
]).get("controller_only").machines;
assert.equal(entry.running, entry.afterRestart, "restart pending");
}
});
});
describe(".handlePluginInstallRequest()", function() {
let host;
let _fetch;
@@ -307,6 +333,10 @@ describe("host/src/Host", function() {
await host.handlePluginInstallRequest(new lib.PluginInstallRequest("restart_test"));
assert.equal(host.config.restartRequired, false);
});
it("leaves the flag alone for a pinned version of an already loaded plugin", async function() {
await host.handlePluginInstallRequest(new lib.PluginInstallRequest("restart_test@1.0.0"));
assert.equal(host.config.restartRequired, false);
});
it("rejects when installs are disabled", async function() {
host.config.set("host.allow_plugin_install", false);
host.config.restartRequired = false; // Setting allow_plugin_install requires a restart
@@ -417,6 +447,20 @@ describe("host/src/Host", function() {
assert.equal(process.exitCode, undefined);
assert.equal(mockHost.shutdownCalled, false);
});
it("rejects while a plugin install or update is running", async function() {
// Held by another live process
await fs.writeFile("npm-operation.lock", `${process.ppid}\n`);
try {
await assert.rejects(
() => mockHost.handleHostRestartRequest(),
/Cannot restart while a plugin install or update is running/
);
} finally {
await fs.rm("npm-operation.lock", { force: true });
}
assert.equal(process.exitCode, undefined);
assert.equal(mockHost.shutdownCalled, false);
});
});
});
});