mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 23:24:52 +09:00
Move save and load into config class
This commit is contained in:
8 files changed
+55
-34
No files matched your search
@@ -346,8 +346,7 @@ async function initialize(): Promise<InitializeParameters> {
|
||||
const controllerConfigPath = args.config;
|
||||
logger.info(`Loading config from ${controllerConfigPath}`);
|
||||
try {
|
||||
let jsonConfig = JSON.parse(await fs.readFile(controllerConfigPath, { encoding: "utf8" }));
|
||||
controllerConfig = lib.ControllerConfig.fromJSON(jsonConfig, "controller", controllerConfigPath);
|
||||
controllerConfig = await lib.ControllerConfig.fromFile("controller", controllerConfigPath);
|
||||
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
@@ -366,9 +365,7 @@ async function initialize(): Promise<InitializeParameters> {
|
||||
let asyncRandomBytes = util.promisify(crypto.randomBytes);
|
||||
let bytes = await asyncRandomBytes(256);
|
||||
controllerConfig.set("controller.auth_secret", bytes.toString("base64"));
|
||||
await lib.safeOutputFile(
|
||||
controllerConfigPath, JSON.stringify(controllerConfig, null, "\t")
|
||||
);
|
||||
await controllerConfig.save();
|
||||
}
|
||||
|
||||
if (command === "config") {
|
||||
|
||||
@@ -581,12 +581,8 @@ export default class Controller {
|
||||
});
|
||||
|
||||
private async _saveDataInternal() {
|
||||
if (this.config.dirty) {
|
||||
this.config.dirty = false;
|
||||
await lib.safeOutputFile(this.config.filepath!, JSON.stringify(this.config, null, "\t"));
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.config.save(),
|
||||
this.systems.save(),
|
||||
this.hosts.save(),
|
||||
this.instances.save(),
|
||||
|
||||
+16
-20
@@ -43,12 +43,13 @@ async function discoverInstances(instancesDir: string) {
|
||||
let instanceInfos = new Map<number, { path: string, config: lib.InstanceConfig }>();
|
||||
for (let entry of await fs.readdir(instancesDir, { withFileTypes: true })) {
|
||||
if (entry.isDirectory()) {
|
||||
let instanceConfig = new lib.InstanceConfig("host");
|
||||
let configPath = path.join(instancesDir, entry.name, "instance.json");
|
||||
let instanceConfig = new lib.InstanceConfig("host", Host.instanceConfigWarning);
|
||||
const instancePath = path.join(instancesDir, entry.name);
|
||||
const configPath = path.join(instancePath, "instance.json");
|
||||
|
||||
try {
|
||||
const jsonConfig = JSON.parse(await fs.readFile(configPath, "utf8"));
|
||||
instanceConfig = lib.InstanceConfig.fromJSON(jsonConfig, "host", configPath);
|
||||
instanceConfig = await lib.InstanceConfig.fromFile("host", configPath);
|
||||
instanceConfig.update(Host.instanceConfigWarning, false); // File might be missing the warning
|
||||
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT") {
|
||||
@@ -64,7 +65,6 @@ async function discoverInstances(instancesDir: string) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let instancePath = path.join(instancesDir, entry.name);
|
||||
logger.verbose(`found instance ${instanceConfig.get("instance.name")} in ${instancePath}`);
|
||||
instanceInfos.set(instanceConfig.get("instance.id"), {
|
||||
path: instancePath,
|
||||
@@ -278,6 +278,10 @@ export default class Host extends lib.Link {
|
||||
_disconnecting = false;
|
||||
_shuttingDown = false;
|
||||
|
||||
static instanceConfigWarning = {
|
||||
"_warning": "Changes to this file will be overwritten by the controller's copy.",
|
||||
};
|
||||
|
||||
static async bootstrap(hostConfig: lib.HostConfig) {
|
||||
const modsDirectory = hostConfig.get("host.mods_directory");
|
||||
await fs.ensureDir(modsDirectory);
|
||||
@@ -652,7 +656,7 @@ export default class Host extends lib.Link {
|
||||
instanceInfo.config.update(config, true, "controller");
|
||||
|
||||
} else {
|
||||
let instanceConfig = new lib.InstanceConfig("host");
|
||||
let instanceConfig = new lib.InstanceConfig("host", Host.instanceConfigWarning);
|
||||
instanceConfig.update(config, false, "controller");
|
||||
|
||||
let instanceDir = await this._createNewInstanceDir(instanceConfig.get("instance.name"));
|
||||
@@ -691,15 +695,7 @@ export default class Host extends lib.Link {
|
||||
await this.sendSaveListUpdate(instanceId, path.join(instanceInfo.path, "saves"));
|
||||
|
||||
// save a copy of the instance config
|
||||
await this.saveInstanceConfig(instanceInfo);
|
||||
}
|
||||
|
||||
async saveInstanceConfig(instanceInfo: { path: string, config: lib.InstanceConfig }) {
|
||||
let warnedOutput = {
|
||||
_warning: "Changes to this file will be overwritten by the controller's copy.",
|
||||
...instanceInfo.config.toJSON(),
|
||||
};
|
||||
await lib.safeOutputFile(instanceInfo.config.filepath!, JSON.stringify(warnedOutput, null, "\t"));
|
||||
await instanceInfo.config.save();
|
||||
}
|
||||
|
||||
async handleInstanceUnassignInternalRequest(request: lib.InstanceUnassignInternalRequest) {
|
||||
@@ -1152,11 +1148,11 @@ export default class Host extends lib.Link {
|
||||
}
|
||||
|
||||
logger.info("Saving config");
|
||||
await lib.safeOutputFile(this.config.filepath!, JSON.stringify(this.config, null, "\t"));
|
||||
for (const instanceInfo of this.instanceInfos.values()) {
|
||||
// Save in case host_assigned_game_port changed in the config.
|
||||
await this.saveInstanceConfig(instanceInfo);
|
||||
}
|
||||
await this.config.save();
|
||||
// Save host side in case host_assigned_game_port changed in the config.
|
||||
await Promise.all(
|
||||
[...this.instanceInfos.values()].map(instanceInfo => instanceInfo.config.save())
|
||||
);
|
||||
|
||||
try {
|
||||
// Clear silly interval in pidfile library.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Configuration classes
|
||||
import { Type, Static } from "@sinclair/typebox";
|
||||
import TypedEventEmitter from "../TypedEventEmitter";
|
||||
import * as fs from "fs-extra";
|
||||
|
||||
import TypedEventEmitter from "../TypedEventEmitter";
|
||||
import isDeepStrictEqual from "../is_deep_strict_equal";
|
||||
import { basicType } from "../helpers";
|
||||
import * as libSchema from "../schema";
|
||||
import { StringEnum } from "../data/composites";
|
||||
import { safeOutputFile } from "../file_ops";
|
||||
|
||||
const ConfigLocation = StringEnum(["controller", "host", "control"]);
|
||||
export type ConfigLocation = Static<typeof ConfigLocation>;
|
||||
@@ -206,7 +208,7 @@ export class Config<
|
||||
private _unknownFields: Record<string, FieldValue> = {};
|
||||
|
||||
/** Set to true when a field in the config is changed. */
|
||||
dirty = false;
|
||||
private dirty = false;
|
||||
/** Set to true when a 'restart required' field in the config is changed. */
|
||||
restartRequired = false;
|
||||
|
||||
@@ -301,8 +303,31 @@ export class Config<
|
||||
}
|
||||
}
|
||||
|
||||
static jsonSchema = ConfigSchema;
|
||||
/**
|
||||
* Create config from file
|
||||
*
|
||||
* @param location - Location used for access control.
|
||||
* @param filepath - Filepath for the config to save to.
|
||||
* @returns Instance of this config
|
||||
*/
|
||||
static async fromFile(location: ConfigLocation, filepath: string) {
|
||||
const fields = await fs.readJson(filepath, { encoding: "utf8" });
|
||||
return this.fromJSON(fields, location, filepath);
|
||||
}
|
||||
|
||||
/** Saves this config to file if there are unsaved changes */
|
||||
async save() {
|
||||
if (!this.dirty) {
|
||||
return;
|
||||
}
|
||||
if (!this.filepath) {
|
||||
throw new Error("Cannot save config which has no filepath");
|
||||
}
|
||||
await safeOutputFile(this.filepath, JSON.stringify(this, null, "\t"));
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
static jsonSchema = ConfigSchema;
|
||||
static validate = libSchema.compile(this.jsonSchema as any);
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { PluginNodeEnvInfo, PluginWebpackEnvInfo } from "../plugin";
|
||||
import { Static } from "@sinclair/typebox";
|
||||
|
||||
type configFromJSON<T> = (...args: Parameters<typeof classes.Config.fromJSON>) => T;
|
||||
type configFromFile<T> = (...args: Parameters<typeof classes.Config.fromFile>) => Promise<T>;
|
||||
|
||||
export interface ControllerConfigFields {
|
||||
"controller.version": string;
|
||||
@@ -39,6 +40,7 @@ export interface ControllerConfigFields {
|
||||
*/
|
||||
export class ControllerConfig extends classes.Config<ControllerConfigFields> {
|
||||
declare static fromJSON: configFromJSON<ControllerConfig>;
|
||||
declare static fromFile: configFromFile<ControllerConfig>;
|
||||
static migrations(config: Static<typeof this.jsonSchema>) {
|
||||
if (config.hasOwnProperty("controller.external_address")) {
|
||||
config["controller.public_url"] = config["controller.external_address"];
|
||||
@@ -246,6 +248,7 @@ export interface HostConfigFields {
|
||||
*/
|
||||
export class HostConfig extends classes.Config<HostConfigFields> {
|
||||
declare static fromJSON: configFromJSON<HostConfig>;
|
||||
declare static fromFile: configFromFile<HostConfig>;
|
||||
static fieldDefinitions: classes.ConfigDefs<HostConfigFields> = {
|
||||
"host.version": {
|
||||
type: "string",
|
||||
@@ -378,6 +381,7 @@ export interface InstanceConfigFields {
|
||||
*/
|
||||
export class InstanceConfig extends classes.Config<InstanceConfigFields> {
|
||||
declare static fromJSON: configFromJSON<InstanceConfig>;
|
||||
declare static fromFile: configFromFile<InstanceConfig>;
|
||||
static migrations(config: Static<typeof this.jsonSchema>) {
|
||||
function boolToEnableDisable(name: string) {
|
||||
if (config.hasOwnProperty(name) && typeof config[name] === "boolean") {
|
||||
@@ -571,6 +575,7 @@ export interface ControlConfigFields {
|
||||
*/
|
||||
export class ControlConfig extends classes.Config<ControlConfigFields> {
|
||||
declare static fromJSON: configFromJSON<ControlConfig>;
|
||||
declare static fromFile: configFromFile<ControlConfig>;
|
||||
static fieldDefinitions: classes.ConfigDefs<ControlConfigFields> = {
|
||||
"control.controller_url": {
|
||||
description: "URL to connect to the controller at",
|
||||
|
||||
@@ -164,7 +164,7 @@ export async function handleConfigCommand(
|
||||
|
||||
try {
|
||||
instance.set(args.field as string, args.value);
|
||||
await libFileOps.safeOutputFile(instance.filepath!, JSON.stringify(instance, null, "\t"));
|
||||
await instance.save();
|
||||
} catch (err) {
|
||||
if (err instanceof libConfig.InvalidField || err instanceof libConfig.InvalidValue) {
|
||||
logger.error(err.message);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"./src/schema.ts",
|
||||
"./src/subscriptions.ts",
|
||||
"./src/zip_ops.ts",
|
||||
"./src/file_ops.ts",
|
||||
"./src/ExponentialBackoff.ts",
|
||||
"./src/RateLimiter.ts",
|
||||
"./src/TypedEventEmitter.ts",
|
||||
|
||||
@@ -15,6 +15,7 @@ describe("Host testing", function() {
|
||||
|
||||
const configPath = path.join(instancePath, "test", "instance.json");
|
||||
const referenceConfig = new lib.InstanceConfig("host", {
|
||||
...Host.instanceConfigWarning,
|
||||
"instance.id": 1,
|
||||
"instance.name": "test",
|
||||
}, configPath);
|
||||
|
||||
Reference in new issue
Block a user