iobroker.js-controller
Version:
Updated by reinstall.js on 2018-06-11T15:19:56.688Z
314 lines (313 loc) • 10.3 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var adapterUpgradeManager_exports = {};
__export(adapterUpgradeManager_exports, {
AdapterUpgradeManager: () => AdapterUpgradeManager
});
module.exports = __toCommonJS(adapterUpgradeManager_exports);
var import_js_controller_common = require("@iobroker/js-controller-common");
var import_node_http = __toESM(require("node:http"), 1);
var import_node_https = __toESM(require("node:https"), 1);
var import_promises = require("node:timers/promises");
var import_js_controller_cli = require("@iobroker/js-controller-cli");
class AdapterUpgradeManager {
/** Wait ms until adapter is stopped */
STOP_TIMEOUT_MS = 3e3;
/** Wait ms for delivery of final response */
SHUTDOWN_TIMEOUT = 1e4;
/** Name of the adapter to upgrade */
adapterName;
/** Desired adapter version */
version;
/** Response send by webserver */
response = {
running: true,
stderr: [],
stdout: []
};
/** Used to stop the stop shutdown timeout */
shutdownAbortController;
/** Logger to log to file and other transports */
logger;
/** The server used for communicating upgrade status */
server;
/** All socket connections of the webserver */
sockets = /* @__PURE__ */ new Set();
/** Name of the host for logging purposes */
hostname = import_js_controller_common.tools.getHostName();
/** The objects DB client */
objects;
/** The states DB client */
states;
/** List of instances which have been stopped */
stoppedInstances = [];
/** If webserver should be started with https */
useHttps;
/** Public certificate name if https is desired */
certPublicName;
/** Private certificate name if https is desired */
certPrivateName;
/** Port where the webserver should be running */
port;
constructor(options) {
this.adapterName = options.adapterName;
this.version = options.version;
this.logger = options.logger;
this.objects = options.objects;
this.states = options.states;
this.useHttps = options.useHttps;
this.port = options.port;
if (options.useHttps) {
this.certPublicName = options.certPublicName;
this.certPrivateName = options.certPrivateName;
}
}
/**
* Stops the adapter and returns ids of stopped instances
*/
async stopAdapter() {
this.stoppedInstances = await this.getAllEnabledInstances();
await this.enableInstances(this.stoppedInstances, false);
await (0, import_promises.setTimeout)(this.STOP_TIMEOUT_MS);
}
/**
* Start all instances which were enabled before the upgrade
*/
async startAdapter() {
await this.enableInstances(this.stoppedInstances, true);
}
/**
* Start or stop given instances
*
* @param instances id of instances which will be stopped
* @param enabled if enable or disable instances
*/
async enableInstances(instances, enabled) {
const ts = Date.now();
for (const instance of instances) {
const updatedObj = {
common: {
enabled
},
from: `system.host.${this.hostname}`,
ts
};
await this.objects.extendObjectAsync(instance, updatedObj);
}
}
/**
* Install given version of adapter
*/
async performUpgrade() {
const processExitHandler = (exitCode) => {
this.log(`Upgrade process exited with code: ${exitCode}`, true);
};
const upgrade = new import_js_controller_cli.Upgrade({
objects: this.objects,
processExit: processExitHandler,
states: this.states,
params: {}
});
try {
await upgrade.upgradeAdapter(void 0, `${this.adapterName}@${this.version}`, true, true, false);
this.response.success = true;
this.log(`Successfully upgraded ${this.adapterName} to version ${this.version}`);
} catch (e) {
this.log(e.message, true);
this.response.success = false;
}
await this.setFinished();
}
/**
* Starts the web server for admin communication either secure or insecure
*/
async startWebServer() {
if (this.useHttps && this.certPublicName && this.certPrivateName) {
await this.startSecureWebServer({
certPublicName: this.certPublicName,
certPrivateName: this.certPrivateName,
port: this.port,
useHttps: true
});
} else {
this.startInsecureWebServer({ port: this.port, useHttps: false });
}
}
/**
* Shuts down the server, restarts the adapter
*/
shutdownServer() {
if (this.shutdownAbortController) {
this.shutdownAbortController.abort();
}
if (!this.server) {
return;
}
this.destroySockets();
this.server.close(async () => {
await this.startAdapter();
this.log("Successfully started adapter");
});
}
/**
* Destroy all sockets, to prevent requests from keeping server alive
*/
destroySockets() {
for (const socket of this.sockets) {
socket.destroy();
this.sockets.delete(socket);
}
}
/**
* This function is called when the webserver receives a message
*
* @param req received message
* @param res server response
*/
webServerCallback(req, res) {
res.writeHead(200);
res.end(JSON.stringify(this.response));
if (!this.response.running) {
this.log("Final information delivered");
this.shutdownServer();
}
}
/**
* Get all instances of the adapter
*/
async getAllEnabledInstances() {
const res = await this.objects.getObjectListAsync({
startkey: `system.adapter.${this.adapterName}.`,
endkey: `system.adapter.${this.adapterName}.\u9999`
});
let enabledInstances = [];
enabledInstances = res.rows.filter((row) => row.value.common.enabled && this.hostname === row.value.common.host).map((row) => row.value._id);
return enabledInstances;
}
/**
* Log via logger and provide the logs for the server too
*
* @param message the message which will be logged
* @param error if it is an error
*/
log(message, error = false) {
if (error) {
this.logger.error(`host.${this.hostname} ${message}`);
this.response.stderr.push(message);
return;
}
this.logger.info(`host.${this.hostname} [WEBSERVER_UPGRADE] (${this.adapterName}) ${message}`);
this.response.stdout.push(message);
}
/**
* Start an insecure web server for admin communication
*
* @param params Web server configuration
*/
startInsecureWebServer(params) {
const { port } = params;
this.server = import_node_http.default.createServer((req, res) => {
this.webServerCallback(req, res);
});
this.monitorSockets(this.server);
this.server.listen(port, () => {
this.log(`Server is running on http://localhost:${port}`);
});
}
/**
* Start a secure web server for admin communication
*
* @param params Web server configuration
*/
async startSecureWebServer(params) {
const { port, certPublicName, certPrivateName } = params;
const { certPublic, certPrivate } = await this.getCertificates({ certPublicName, certPrivateName });
this.server = import_node_https.default.createServer({ key: certPrivate, cert: certPublic }, (req, res) => {
this.webServerCallback(req, res);
});
this.monitorSockets(this.server);
this.server.listen(port, () => {
this.log(`Server is running on http://localhost:${port}`);
});
}
/**
* Keep track of all existing sockets
*
* @param server the webserver
*/
monitorSockets(server) {
server.on("connection", (socket) => {
this.sockets.add(socket);
server.once("close", () => {
this.sockets.delete(socket);
});
});
}
/**
* Get certificates from the DB
*
* @param params certificate information
*/
async getCertificates(params) {
const { certPublicName, certPrivateName } = params;
const obj = await this.objects.getObjectAsync("system.certificates");
if (!obj) {
throw new Error("No certificates found");
}
const certs = obj.native.certificates;
return { certPrivate: certs[certPrivateName], certPublic: certs[certPublicName] };
}
/**
* Tells the upgrade manager, that server can be shut down on next response or on timeout
*/
async setFinished() {
this.response.running = false;
await this.startShutdownTimeout();
}
/**
* Start a timeout which starts adapter and shuts down the server if expired
*/
async startShutdownTimeout() {
this.shutdownAbortController = new AbortController();
try {
await (0, import_promises.setTimeout)(this.SHUTDOWN_TIMEOUT, null, { signal: this.shutdownAbortController.signal });
this.log("Timeout expired, initializing shutdown");
this.shutdownServer();
} catch (e) {
if (e.code !== "ABORT_ERR") {
this.log(e.message, true);
}
}
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AdapterUpgradeManager
});
//# sourceMappingURL=adapterUpgradeManager.js.map