From 63e30dd0ee2c19de402e4bd0da1ae7b1dcf9b896 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Wed, 21 May 2025 17:07:24 +0100 Subject: [PATCH] Add missing migrations for system info and tests (#766) * Add missing migration for SystemInfo * Add missing migration tests --- packages/controller/index.ts | 1 + packages/controller/src/Controller.ts | 20 +++- test/controller/Controller.js | 129 +++++++++++++++++++++++++- test/messages/controller.test.js | 2 +- test/messages/host.test.js | 2 +- test/messages/plugin.test.js | 4 +- 6 files changed, 149 insertions(+), 9 deletions(-) diff --git a/packages/controller/index.ts b/packages/controller/index.ts index caebb912..6819b7cc 100644 --- a/packages/controller/index.ts +++ b/packages/controller/index.ts @@ -5,6 +5,7 @@ export { default as ControllerRouter } from "./src/ControllerRouter"; export { default as ControlConnection } from "./src/ControlConnection"; export { default as HostConnection } from "./src/HostConnection"; export { default as InstanceInfo } from "./src/InstanceInfo"; +export { default as HostInfo } from "./src/HostInfo"; export { default as BaseControllerPlugin } from "./src/BaseControllerPlugin"; export { default as UserManager } from "./src/UserManager"; export { default as WsServer } from "./src/WsServer"; diff --git a/packages/controller/src/Controller.ts b/packages/controller/src/Controller.ts index 6b60ac68..f600bd65 100644 --- a/packages/controller/src/Controller.ts +++ b/packages/controller/src/Controller.ts @@ -104,7 +104,8 @@ export default class Controller { const systems = new lib.SubscribableDatastore(...await new lib.JsonIdDatastoreProvider( path.join(databaseDirectory, "systems.json"), - lib.SystemInfo.fromJSON.bind(lib.SystemInfo) + lib.SystemInfo.fromJSON.bind(lib.SystemInfo), + this.migrateSystems, ).bootstrap()); const hosts = new lib.SubscribableDatastore(...await new lib.JsonIdDatastoreProvider( @@ -597,10 +598,23 @@ export default class Controller { await lib.invokeHook(this.plugins, "onSaveData"); } + static migrateSystems(rawJson: unknown[]): Static[] { + const serialized = rawJson as Static[]; + return serialized.map(json => { + if (!json.canRestart) { // Added in 2.0.0.alpha.17 + json.canRestart = false; + } + if (!json.restartRequired) { // Added in 2.0.0.alpha.21 + json.restartRequired = false; + } + return json; + }); + } + static migrateHosts(rawJson: unknown[]): Static[] { let serialized = rawJson as any; - // migrate from pre alpha.19 format + // New format 2.0.0.alpha.19 if (serialized.length && serialized[0] instanceof Array) { serialized = serialized.map((e: any) => e[1]); } @@ -619,7 +633,7 @@ export default class Controller { static migrateInstances(rawJson: unknown[]): Static[] { const serialized = rawJson as Static[]; return serialized.map(json => { - if (!json.config) { // migrate: from pre Alpha 14 format. + if (!json.config) { // New format 2.0.0.alpha.14 return { config: json as any, status: "running" }; // Use running to force updatedAtMs } return json; diff --git a/test/controller/Controller.js b/test/controller/Controller.js index 1cfc4814..44321719 100644 --- a/test/controller/Controller.js +++ b/test/controller/Controller.js @@ -1,7 +1,7 @@ "use strict"; const assert = require("assert").strict; -const { Controller } = require("@clusterio/controller"); -const { ControllerConfig, Address, RequestError } = require("@clusterio/lib"); +const { Controller, HostInfo, InstanceInfo } = require("@clusterio/controller"); +const { ControllerConfig, Address, RequestError, InstanceConfig, HostConfig, SystemInfo } = require("@clusterio/lib"); const { EventEmitter } = require("stream"); class MockEvent {} @@ -189,5 +189,130 @@ describe("controller/src/Controller", function() { assert(controller.instances.dirty === true, "dirty flag was not set"); }); }); + describe(".finaliseHosts()", function() { + it("resets the connected state to false", function() { + const hostInfo = new HostInfo(1, "", "", new Map(), true); + Controller.finaliseHosts(hostInfo); + assert.equal(hostInfo.connected, false); + assert(hostInfo.updatedAtMs > 0, "updatedAtMs not incremented"); + }); + it("does not increment updatedAtMs when there are no changes", function() { + const hostInfo = new HostInfo(1, "", "", new Map(), false); + Controller.finaliseHosts(hostInfo); + assert.equal(hostInfo.connected, false); + assert(hostInfo.updatedAtMs === 0, "updatedAtMs incremented"); + }); + }); + describe(".finaliseInstances()", function() { + it("resets the stats for assigned instances", function() { + const instanceConfig = new InstanceConfig("controller", { "instance.assigned_host": 1 }); + const instanceInfo = new InstanceInfo(instanceConfig, "running"); + Controller.finaliseInstances(instanceInfo); + assert.equal(instanceInfo.status, "unknown"); + assert(instanceInfo.updatedAtMs > 0, "updatedAtMs not incremented"); + }); + it("resets the status for unassigned instances", function() { + const instanceConfig = new InstanceConfig("controller", { "instance.assigned_host": null }); + const instanceInfo = new InstanceInfo(instanceConfig, "running"); + Controller.finaliseInstances(instanceInfo); + assert.equal(instanceInfo.status, "unassigned"); + assert(instanceInfo.updatedAtMs > 0, "updatedAtMs not incremented"); + }); + it("does not update updatedAtMs when there are no changes (assigned)", function() { + const instanceConfig = new InstanceConfig("controller", { "instance.assigned_host": 1 }); + const instanceInfo = new InstanceInfo(instanceConfig, "unknown"); + Controller.finaliseInstances(instanceInfo); + assert.equal(instanceInfo.status, "unknown"); + assert(instanceInfo.updatedAtMs === 0, "updatedAtMs incremented"); + }); + it("does not update updatedAtMs when there are no changes (unassigned)", function() { + const instanceConfig = new InstanceConfig("controller", { "instance.assigned_host": null }); + const instanceInfo = new InstanceInfo(instanceConfig, "unassigned"); + Controller.finaliseInstances(instanceInfo); + assert.equal(instanceInfo.status, "unassigned"); + assert(instanceInfo.updatedAtMs === 0, "updatedAtMs incremented"); + }); + }); + }); + describe("migrations", function() { + describe("SystemInfo", function() { + it("migrates 'canRestart' from undefined to false", function() { // Alpha 17 + const result = Controller.migrateSystems([ + {}, { canRestart: true }, { canRestart: false }, + ]); + assert.equal(result[0].canRestart, false); + assert.equal(result[1].canRestart, true); + assert.equal(result[2].canRestart, false); + }); + it("migrates 'restartRequired' from undefined to false", function() { // Alpha 21 + const result = Controller.migrateSystems([ + {}, { restartRequired: true }, { restartRequired: false }, + ]); + assert.equal(result[0].restartRequired, false); + assert.equal(result[1].restartRequired, true); + assert.equal(result[2].restartRequired, false); + }); + it("does nothing for upto date data", function() { + const systemInfo = new SystemInfo( + 1, "name", "n", "k", "m", "cm", "cr", "mc", "ma", "dc", "da", false, false, 0, false + ); + const jsonString = JSON.stringify(systemInfo); + const systemInfoJson = JSON.parse(jsonString); + const systemInfoJsonCopy = JSON.parse(jsonString); + const result = Controller.migrateSystems([ + systemInfoJson, systemInfoJson, systemInfoJson, + ]); + assert.deepEqual(result, [ + systemInfoJsonCopy, systemInfoJsonCopy, systemInfoJsonCopy, + ]); + }); + }); + describe("HostInfo", function() { + it("migrates to the new format", function() { // Alpha 19 + const result = Controller.migrateHosts([ + ["", "foo"], ["", "bar"], ["", "baz"], + ]); + assert.deepEqual(result, [ + "foo", "bar", "baz", + ]); + }); + it("does nothing for upto date data", function() { + const hostInfo = new HostInfo(1, "", "", new Map()); + const jsonString = JSON.stringify(hostInfo); + const hostInfoJson = JSON.parse(jsonString); + const hostInfoJsonCopy = JSON.parse(jsonString); + const result = Controller.migrateHosts([ + hostInfoJson, hostInfoJson, hostInfoJson, + ]); + assert.deepEqual(result, [ + hostInfoJsonCopy, hostInfoJsonCopy, hostInfoJsonCopy, + ]); + }); + }); + describe("InstanceInfo", function() { + it("migrates to the new format", function() { // Alpha 14 + const result = Controller.migrateInstances([ + { name: "foo" }, { name: "bar" }, { name: "baz" }, + ]); + assert.deepEqual(result, [ + { config: { name: "foo" }, status: "running" }, + { config: { name: "bar" }, status: "running" }, + { config: { name: "baz" }, status: "running" }, + ]); + }); + it("does nothing for upto date data", function() { + const instanceConfig = new InstanceConfig("controller"); + const instanceInfo = new InstanceInfo(instanceConfig, "unknown"); + const jsonString = JSON.stringify(instanceInfo); + const instanceInfoJson = JSON.parse(jsonString); + const instanceInfoJsonCopy = JSON.parse(jsonString); + const result = Controller.migrateInstances([ + instanceInfoJson, instanceInfoJson, instanceInfoJson, + ]); + assert.deepEqual(result, [ + instanceInfoJsonCopy, instanceInfoJsonCopy, instanceInfoJsonCopy, + ]); + }); + }); }); }); diff --git a/test/messages/controller.test.js b/test/messages/controller.test.js index 89f43ff4..cb2c2efd 100644 --- a/test/messages/controller.test.js +++ b/test/messages/controller.test.js @@ -16,7 +16,7 @@ describe("messages/controller", function() { lib.Address.fromShorthand("controller"), lib.Address.fromShorthand({ controlId: 1 }), ); - controller = new Controller(lib.logger, [], "", controllerConfig); + controller = new Controller(lib.logger, [], controllerConfig); const user = new ControllerUser(controller.userManager, undefined, "test"); controlConnection = new ControlConnection({ version: "2.0.0" }, connection, controller, user, 1); }); diff --git a/test/messages/host.test.js b/test/messages/host.test.js index 47a250ac..83938147 100644 --- a/test/messages/host.test.js +++ b/test/messages/host.test.js @@ -14,7 +14,7 @@ describe("messages/controller", function() { lib.Address.fromShorthand({ hostId: 1 }), lib.Address.fromShorthand("controller"), ); - host = new Host(hostConnector, "", hostConfig, undefined, []); + host = new Host(hostConnector, hostConfig, undefined, []); }); describe("HostUpdateRequest", function() { diff --git a/test/messages/plugin.test.js b/test/messages/plugin.test.js index c4a2f466..63d3b5fb 100644 --- a/test/messages/plugin.test.js +++ b/test/messages/plugin.test.js @@ -19,7 +19,7 @@ describe("messages/plugin", function() { lib.Address.fromShorthand("controller"), lib.Address.fromShorthand({ controlId: 1 }), ); - controller = new Controller(lib.logger, [], "", controllerConfig); + controller = new Controller(lib.logger, [], controllerConfig); const user = new ControllerUser(controller.userManager, undefined, "test"); controlConnection = new ControlConnection({ version: "2.0.0" }, connection, controller, user, 1); @@ -28,7 +28,7 @@ describe("messages/plugin", function() { lib.Address.fromShorthand({ hostId: 1 }), lib.Address.fromShorthand("controller"), ); - host = new Host(hostConnector, "", hostConfig, undefined, []); + host = new Host(hostConnector, hostConfig, undefined, []); }); describe("PluginDetails", function() {