From f6ab82569261bd4f18cccdf95cd229ca57b4fd12 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Mon, 10 May 2021 15:19:53 +0200 Subject: [PATCH] Add status events for create/delete instance Notify plugins of status changes when instances are created and deleted. --- packages/lib/plugin.js | 18 +++++++++++++----- packages/master/src/ControlConnection.js | 9 ++++++++- packages/master/src/SlaveConnection.js | 19 +++++++++---------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/lib/plugin.js b/packages/lib/plugin.js index af7b677e..3f76abd0 100644 --- a/packages/lib/plugin.js +++ b/packages/lib/plugin.js @@ -252,15 +252,18 @@ class BaseMasterPlugin { /** * Called when the status of an instance changes * - * Invoked when the master server has received notice from a slave that - * the running status of an instance has changed. The possible statuses - * that can be notified about are: - * - `stopped`: Instance is no longer running. + * Invoked when the master server has changed the status of an instance + * or received notice from a slave that the status of an instance has + * changed. The possible statuses that can be notified about are: + * - `unassigned:`: Instance is no longer asssigned to a slave. + * - `stopped`: Instance is no longer running or was just assigned to a + * slave. * - `starting`: Instance is in the process of starting up. * - `running`: Instance startup completed and is now running. * - `creating_save`: Instance is in the process of creating a save. * - `exporting_data`: Instance is in the process of exporting item * icons and locale data. + * - `deleted`: Instance was deleted. * * On master startup all known instances gets the status `unknown` if * they are assigned to a slave, when the slave then connects to the @@ -268,12 +271,17 @@ class BaseMasterPlugin { * is invoked for all those instances. You can detect this situation by * checking if prev equals `unknown`. * + * When instances are created on the master they will notify of a status + * change with prev set to null. While the status of new instances is + * in most cases `unassigned` it's possible for the created instance to + * start with any state in some slave connection corner cases. + * * Note that it's possible for status change notification to get lost in * the case of network outages if reconnect fails to re-establish the * session between the master server and the slave. * * @param {Object} instance - the instance that changed. - * @param {string} prev - the previous status of the instance. + * @param {?string} prev - the previous status of the instance. */ async onInstanceStatusChanged(instance, prev) { } diff --git a/packages/master/src/ControlConnection.js b/packages/master/src/ControlConnection.js index af07f3b6..85e818bd 100644 --- a/packages/master/src/ControlConnection.js +++ b/packages/master/src/ControlConnection.js @@ -8,6 +8,7 @@ const libErrors = require("@clusterio/lib/errors"); const libLink = require("@clusterio/lib/link"); const { logger } = require("@clusterio/lib/logging"); const libLoggingUtils = require("@clusterio/lib/logging_utils"); +const libPlugin = require("@clusterio/lib/plugin"); const libPrometheus = require("@clusterio/lib/prometheus"); const libUsers = require("@clusterio/lib/users"); @@ -170,7 +171,9 @@ class ControlConnection extends BaseConnection { }; instanceConfig.set("factorio.settings", settings); - this._master.instances.set(instanceId, { config: instanceConfig, status: "unassigned" }); + let instance = { config: instanceConfig, status: "unassigned" }; + this._master.instances.set(instanceId, instance); + await libPlugin.invokeHook(this._master.plugins, "onInstanceStatusChanged", instance, null); } async deleteInstanceRequestHandler(message, request) { @@ -183,6 +186,10 @@ class ControlConnection extends BaseConnection { await this.forwardRequestToInstance(message, request); } this._master.instances.delete(message.data.instance_id); + + let prev = instance.status; + instance.status = "deleted"; + await libPlugin.invokeHook(this._master.plugins, "onInstanceStatusChanged", instance, prev); } async getInstanceConfigRequestHandler(message) { diff --git a/packages/master/src/SlaveConnection.js b/packages/master/src/SlaveConnection.js index 2ef9d408..5dbea73f 100644 --- a/packages/master/src/SlaveConnection.js +++ b/packages/master/src/SlaveConnection.js @@ -71,9 +71,9 @@ class SlaveConnection extends BaseConnection { } // Assign instances the slave has but master does not - for (let instance of message.data.instances) { + for (let instanceData of message.data.instances) { let instanceConfig = new libConfig.InstanceConfig("master"); - await instanceConfig.load(instance.serialized_config, "slave"); + await instanceConfig.load(instanceData.serialized_config, "slave"); let masterInstance = this._master.instances.get(instanceConfig.get("instance.id")); if (masterInstance) { @@ -86,24 +86,23 @@ class SlaveConnection extends BaseConnection { } // Already have this instance, update state instead - if (masterInstance.status !== instance.status) { + if (masterInstance.status !== instanceData.status) { let prev = masterInstance.status; - masterInstance.status = instance.status; - logger.verbose(`Instance ${instanceConfig.get("instance.name")} State: ${instance.status}`); - await libPlugin.invokeHook(this._master.plugins, "onInstanceStatusChanged", instance, prev); + masterInstance.status = instanceData.status; + logger.verbose(`Instance ${instanceConfig.get("instance.name")} State: ${instanceData.status}`); + await libPlugin.invokeHook(this._master.plugins, "onInstanceStatusChanged", masterInstance, prev); } continue; } instanceConfig.set("instance.assigned_slave", this._id); - this._master.instances.set(instanceConfig.get("instance.id"), { - config: instanceConfig, - status: instance.status, - }); + let newInstance = { config: instanceConfig, status: instanceData.status }; + this._master.instances.set(instanceConfig.get("instance.id"), newInstance); await libLink.messages.assignInstance.send(this, { instance_id: instanceConfig.get("instance.id"), serialized_config: instanceConfig.serialize("slave"), }); + await libPlugin.invokeHook(this._master.plugins, "onInstanceStatusChanged", newInstance, null); } // Push lists to make sure they are in sync.