mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 23:24:52 +09:00
Refactor InstanceInfo
Refactor to store instance status, gamePort, and updatedAt to disk for instances. This enables InstanceDetails to properly record when it was updated.
This commit is contained in:
1 parent
c248363895
commit
d186f85773
7 files changed
+109
-118
No files matched your search
@@ -228,49 +228,11 @@ export default class ControlConnection extends BaseConnection {
|
||||
}
|
||||
|
||||
async handleInstanceDetailsGetRequest(request: lib.InstanceDetailsGetRequest) {
|
||||
let instance = this._controller.getRequestInstance(request.instanceId);
|
||||
|
||||
let assigned_host: number|null|undefined = instance.config.get("instance.assigned_host");
|
||||
if (assigned_host === null) {
|
||||
assigned_host = undefined;
|
||||
}
|
||||
|
||||
let game_port: number|null|undefined = instance.game_port;
|
||||
if (game_port === null) {
|
||||
game_port = undefined;
|
||||
}
|
||||
|
||||
return new lib.InstanceDetails(
|
||||
instance.config.get("instance.name"),
|
||||
instance.id,
|
||||
assigned_host,
|
||||
game_port,
|
||||
instance.status,
|
||||
);
|
||||
return this._controller.getRequestInstance(request.instanceId).toInstanceDetails();
|
||||
}
|
||||
|
||||
async handleInstanceDetailsListRequest() {
|
||||
let list = [];
|
||||
for (let instance of this._controller.instances.values()) {
|
||||
let assigned_host: number|null|undefined = instance.config.get("instance.assigned_host");
|
||||
if (assigned_host === null) {
|
||||
assigned_host = undefined;
|
||||
}
|
||||
|
||||
let game_port: number|null|undefined = instance.game_port;
|
||||
if (game_port === null) {
|
||||
game_port = undefined;
|
||||
}
|
||||
|
||||
list.push(new lib.InstanceDetails(
|
||||
instance.config.get("instance.name"),
|
||||
instance.id,
|
||||
assigned_host,
|
||||
game_port,
|
||||
instance.status,
|
||||
));
|
||||
}
|
||||
return list;
|
||||
return [...this._controller.instances.values()].map(instance => instance.toInstanceDetails());
|
||||
}
|
||||
|
||||
// XXX should probably add a hook for host reuqests?
|
||||
|
||||
@@ -17,7 +17,7 @@ import * as lib from "@clusterio/lib";
|
||||
const { logger, Summary, Gauge } = lib;
|
||||
|
||||
import HttpCloser from "./HttpCloser";
|
||||
import InstanceInfo, { InstanceStatus } from "./InstanceInfo";
|
||||
import InstanceInfo from "./InstanceInfo";
|
||||
import * as metrics from "./metrics";
|
||||
import * as routes from "./routes";
|
||||
import ControllerUser from "./ControllerUser";
|
||||
@@ -478,16 +478,22 @@ export default class Controller {
|
||||
|
||||
let instances = new Map();
|
||||
try {
|
||||
let serialized = JSON.parse(await fs.readFile(filePath, { encoding: "utf8" }));
|
||||
for (let serializedConfig of serialized) {
|
||||
let serialized = JSON.parse(
|
||||
await fs.readFile(filePath, { encoding: "utf8" })
|
||||
) as Static<typeof InstanceInfo.jsonSchema>[];
|
||||
for (let json of serialized) {
|
||||
if (!json.config) { // migrate: from pre Alpha 14 format.
|
||||
json = { config: json, status: "running" }; // Use running to force updatedAt
|
||||
}
|
||||
let instanceConfig = new lib.InstanceConfig("controller");
|
||||
await instanceConfig.load(serializedConfig);
|
||||
let status = instanceConfig.get("instance.assigned_host") === null ? "unassigned" : "unknown";
|
||||
let instance = new InstanceInfo({
|
||||
config: instanceConfig,
|
||||
status: status as InstanceStatus,
|
||||
});
|
||||
instances.set(instanceConfig.get("instance.id"), instance);
|
||||
await instanceConfig.load(json.config as lib.SerializedConfig);
|
||||
let instance = InstanceInfo.fromJSON(json, instanceConfig);
|
||||
const status = instance.config.get("instance.assigned_host") === null ? "unassigned" : "unknown";
|
||||
if (instance.status !== status) {
|
||||
instance.status = status;
|
||||
instance.updatedAt = Date.now();
|
||||
}
|
||||
instances.set(instance.id, instance);
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
@@ -501,11 +507,7 @@ export default class Controller {
|
||||
}
|
||||
|
||||
static async saveInstances(filePath: string, instances: Map<number, InstanceInfo>) {
|
||||
let serialized = [];
|
||||
for (const instance of instances.values()) {
|
||||
serialized.push(instance.config.serialize());
|
||||
}
|
||||
|
||||
let serialized = [...instances.values()].map(instance => instance.toJSON());
|
||||
await lib.safeOutputFile(filePath, JSON.stringify(serialized, null, "\t"));
|
||||
}
|
||||
|
||||
@@ -692,7 +694,7 @@ export default class Controller {
|
||||
};
|
||||
instanceConfig.set("factorio.settings", settings);
|
||||
|
||||
let instance = new InstanceInfo({ config: instanceConfig, status: "unassigned" });
|
||||
let instance = new InstanceInfo(instanceConfig, "unassigned", undefined, Date.now());
|
||||
this.instances.set(instanceId, instance);
|
||||
this.instancesDirty = true;
|
||||
await lib.invokeHook(this.plugins, "onInstanceStatusChanged", instance);
|
||||
@@ -772,6 +774,7 @@ export default class Controller {
|
||||
|
||||
let prev = instance.status;
|
||||
instance.status = "deleted";
|
||||
instance.updatedAt = Date.now();
|
||||
this.instanceDetailsUpdated([instance]);
|
||||
await lib.invokeHook(this.plugins, "onInstanceStatusChanged", instance, prev);
|
||||
}
|
||||
@@ -799,6 +802,7 @@ export default class Controller {
|
||||
|
||||
addInstanceHooks(instance: InstanceInfo) {
|
||||
instance.config.on("fieldChanged", (group: lib.ConfigGroup, field: string, prev: any) => {
|
||||
instance.updatedAt = Date.now();
|
||||
if (group.name === "instance" && field === "name") {
|
||||
this.instanceDetailsUpdated([instance]);
|
||||
}
|
||||
@@ -809,29 +813,7 @@ export default class Controller {
|
||||
}
|
||||
|
||||
instanceDetailsUpdated(instances: InstanceInfo[]) {
|
||||
const now = Date.now();
|
||||
const updates: lib.InstanceDetails[] = [];
|
||||
for (const instance of instances) {
|
||||
let assigned_host: number|null|undefined = instance.config.get("instance.assigned_host");
|
||||
if (assigned_host === null) {
|
||||
assigned_host = undefined;
|
||||
}
|
||||
|
||||
let game_port: number|null|undefined = instance.game_port;
|
||||
if (game_port === null) {
|
||||
game_port = undefined;
|
||||
}
|
||||
|
||||
updates.push(new lib.InstanceDetails(
|
||||
instance.config.get("instance.name"),
|
||||
instance.id,
|
||||
assigned_host,
|
||||
game_port,
|
||||
instance.status,
|
||||
now,
|
||||
));
|
||||
}
|
||||
|
||||
const updates = instances.map(instance => instance.toInstanceDetails());
|
||||
this.subscriptions.broadcast(new lib.InstanceDetailsUpdatesEvent(updates));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as lib from "@clusterio/lib";
|
||||
const { logger } = lib;
|
||||
|
||||
import BaseConnection from "./BaseConnection";
|
||||
import InstanceInfo, { InstanceStatus } from "./InstanceInfo";
|
||||
import InstanceInfo from "./InstanceInfo";
|
||||
import HostInfo from "./HostInfo";
|
||||
|
||||
|
||||
@@ -176,8 +176,9 @@ export default class HostConnection extends BaseConnection {
|
||||
}
|
||||
|
||||
let prev = instance.status;
|
||||
instance.status = request.status as lib.InstanceStatus;
|
||||
instance.game_port = request.gamePort || null;
|
||||
instance.status = request.status;
|
||||
instance.gamePort = request.gamePort;
|
||||
instance.updatedAt = Date.now();
|
||||
logger.verbose(`Instance ${instance.config.get("instance.name")} State: ${instance.status}`);
|
||||
this._controller.instanceDetailsUpdated([instance]);
|
||||
await lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", instance, prev);
|
||||
@@ -212,7 +213,9 @@ export default class HostConnection extends BaseConnection {
|
||||
// Already have this instance, update state instead
|
||||
if (controllerInstance.status !== instanceData.status) {
|
||||
let prev = controllerInstance.status;
|
||||
controllerInstance.status = instanceData.status as InstanceStatus;
|
||||
controllerInstance.status = instanceData.status;
|
||||
controllerInstance.gamePort = instanceData.gamePort;
|
||||
controllerInstance.updatedAt = Date.now();
|
||||
logger.verbose(`Instance ${instanceConfig.get("instance.name")} State: ${instanceData.status}`);
|
||||
instanceUpdates.push(controllerInstance);
|
||||
await lib.invokeHook(
|
||||
@@ -224,7 +227,10 @@ export default class HostConnection extends BaseConnection {
|
||||
|
||||
instanceConfig.set("instance.assigned_host", this._id);
|
||||
let newInstance = new InstanceInfo(
|
||||
{ config: instanceConfig, status: instanceData.status as InstanceStatus }
|
||||
instanceConfig,
|
||||
instanceData.status,
|
||||
instanceData.gamePort,
|
||||
Date.now(),
|
||||
);
|
||||
this._controller.instances.set(instanceConfig.get("instance.id"), newInstance);
|
||||
this._controller.instancesDirty = true;
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
import type * as lib from "@clusterio/lib";
|
||||
|
||||
/**
|
||||
* Current status of the instance. One of:
|
||||
* - `unknown`: Instance is assigned to a host but this host is currently
|
||||
* not connected to the contreller.
|
||||
* - `unassigned`: Instance is not assigned to a a host and exists only on
|
||||
* the controller.
|
||||
* - `stopped`: Instance is stopped.
|
||||
* - `starting`: Instance is in the process of starting up.
|
||||
* - `running`: Instance is running normally.
|
||||
* - `stopping`: Instance is in the process of stopping.
|
||||
* - `creating_save`: Instance is in the process of creating a save.
|
||||
* - `exporting_data`: Instance is in the process of exporting game data.
|
||||
* - `deleted`: Instance has been deleted.
|
||||
*/
|
||||
export type InstanceStatus =
|
||||
"unknown" | "unassigned" | "stopped" | "starting" | "running" |
|
||||
"stopping" | "creating_save" | "exporting_data" | "deleted";
|
||||
import * as lib from "@clusterio/lib";
|
||||
import { Static, Type } from "@sinclair/typebox";
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,25 +7,56 @@ export type InstanceStatus =
|
||||
* @alias module:controller/src/InstanceInfo
|
||||
*/
|
||||
export default class InstanceInfo {
|
||||
config: lib.InstanceConfig;
|
||||
status: InstanceStatus;
|
||||
game_port: number | null = null;
|
||||
|
||||
constructor(
|
||||
{ config, status }:
|
||||
{ config: lib.InstanceConfig, status: InstanceStatus }
|
||||
public config: lib.InstanceConfig,
|
||||
public status: lib.InstanceStatus,
|
||||
public gamePort?: number,
|
||||
public updatedAt = 0,
|
||||
) {
|
||||
this.config = config;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
static jsonSchema = Type.Object({
|
||||
"config": Type.Object({}),
|
||||
"status": lib.InstanceStatus,
|
||||
"gamePort": Type.Optional(Type.Number()),
|
||||
"updatedAt": Type.Optional(Type.Number()),
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof this.jsonSchema>, config: lib.InstanceConfig) {
|
||||
return new this(
|
||||
config,
|
||||
json.status,
|
||||
json.gamePort,
|
||||
json.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
toJSON(): Static<typeof InstanceInfo.jsonSchema> {
|
||||
return {
|
||||
config: this.config.serialize(),
|
||||
status: this.status,
|
||||
gamePort: this.gamePort,
|
||||
updatedAt: this.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
toInstanceDetails() {
|
||||
return new lib.InstanceDetails(
|
||||
this.config.get("instance.name"),
|
||||
this.id,
|
||||
this.config.get("instance.assigned_host") ?? undefined,
|
||||
this.gamePort,
|
||||
this.status,
|
||||
this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
get isDeleted() {
|
||||
return this.status === "deleted";
|
||||
}
|
||||
|
||||
/** Shorthand for `instance.config.get("instance.id")` */
|
||||
get id():number {
|
||||
return this.config.get("instance.id");
|
||||
|
||||
@@ -913,6 +913,7 @@ export default class Host extends lib.Link {
|
||||
list.push(new lib.RawInstanceInfo(
|
||||
instanceInfo.config.serialize("controller"),
|
||||
instanceConnection ? instanceConnection.instance.status : "stopped",
|
||||
instanceConnection ? instanceConnection.instance.server.gamePort : undefined,
|
||||
));
|
||||
}
|
||||
await this.send(new lib.InstancesUpdateRequest(list));
|
||||
|
||||
@@ -446,7 +446,7 @@ rcon.print(game.table_to_json(players))`.replace(/\r?\n/g, " ");
|
||||
new lib.InstanceStatusChangedEvent(
|
||||
this.id,
|
||||
status,
|
||||
this.server.gamePort || this.config.get("factorio.game_port") || undefined,
|
||||
status !== "stopped" ? this.server.gamePort : undefined,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,34 @@ import type { MessageRequest } from "./messages_core";
|
||||
import { CollectorResultSerialized } from "../prometheus";
|
||||
|
||||
|
||||
export type InstanceStatus =
|
||||
"unknown" | "unassigned" | "stopped" | "starting" | "running"
|
||||
| "stopping" | "creating_save" | "exporting_data" | "deleted"
|
||||
;
|
||||
export const InstanceStatus = StringEnum([
|
||||
"unknown",
|
||||
"unassigned",
|
||||
"stopped",
|
||||
"starting",
|
||||
"running",
|
||||
"stopping",
|
||||
"creating_save",
|
||||
"exporting_data",
|
||||
"deleted",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Current status of the instance. One of:
|
||||
* - `unknown`: Instance is assigned to a host but this host is currently
|
||||
* not connected to the contreller.
|
||||
* - `unassigned`: Instance is not assigned to a a host and exists only on
|
||||
* the controller.
|
||||
* - `stopped`: Instance is stopped.
|
||||
* - `starting`: Instance is in the process of starting up.
|
||||
* - `running`: Instance is running normally.
|
||||
* - `stopping`: Instance is in the process of stopping.
|
||||
* - `creating_save`: Instance is in the process of creating a save.
|
||||
* - `exporting_data`: Instance is in the process of exporting game data.
|
||||
* - `deleted`: Instance has been deleted.
|
||||
*/
|
||||
export type InstanceStatus = Static<typeof InstanceStatus>;
|
||||
|
||||
export class InstanceDetails {
|
||||
constructor(
|
||||
public name: string,
|
||||
@@ -653,7 +677,8 @@ export class RawInstanceInfo {
|
||||
|
||||
constructor(
|
||||
public config: object,
|
||||
public status: string,
|
||||
public status: InstanceStatus,
|
||||
public gamePort: undefined | number,
|
||||
) { }
|
||||
|
||||
static jsonSchema = Type.Object({
|
||||
@@ -661,10 +686,11 @@ export class RawInstanceInfo {
|
||||
"status": StringEnum([
|
||||
"stopped", "starting", "running", "stopping", "creating_save", "exporting_data",
|
||||
]),
|
||||
"gamePort": Type.Optional(Type.Number()),
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof this.jsonSchema>) {
|
||||
return new this(json.config, json.status);
|
||||
return new this(json.config, json.status, json.gamePort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,7 +783,7 @@ export class InstanceStatusChangedEvent {
|
||||
|
||||
constructor(
|
||||
public instanceId: number,
|
||||
public status: string,
|
||||
public status: InstanceStatus,
|
||||
public gamePort?: number,
|
||||
) { }
|
||||
|
||||
|
||||
Reference in new issue
Block a user