UNPKG

microservicebus-core

Version:

node.js node for microServiceBus.com. Please visit https://microservicebus.com for more information.

1,095 lines (1,011 loc) 108 kB
/* The MIT License (MIT) Copyright (c) 2014 microServiceBus.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* jshint node: true */ /* jshint esversion: 6 */ /* jshint strict:false */ 'use strict'; var colors = require('colors'); var moment = require('moment'); var extend = require('extend'); var os = require("os"); var fs = require('fs'); var path = require('path'); var util = require('./utils.js'); var guid = require('uuid'); var pjson = require('../package.json'); const { utils } = require('mocha'); const { exec } = require('child_process'); var webRequest = require('./WebRequest'); const { Agent } = require("https"); function MicroServiceBusHost(settingsHelper) { var self = this; const DEBUGEXPIRATION = 1000 * 60 * 30 // Used for automaticly switching off debug output // Callbacks this.onStarted = null; this.onStopped = null; this.onUnitTestComlete = null; this.proxyService = null; // Handle settings var microServiceBusNode; var microServiceBusDAM; var microServiceBusDBusInterface; var _temporaryVerificationCode; var _useMacAddress = false; var _useAssert = false; var _client; var _heartBeatInterval; var _lastHeartBeatReceived = true; var _missedHeartBeats = 0; var _logStream; var _existingNodeName; var _debugModeEnabled = undefined; var _lastKnownLocation; var _locationNotification; var _locationNotificationInterval = 900000; // Report location every 15 minutes var _signInState = "NONE"; var _lastConnectedTime; var _vulnerabilitiesScanJob; var _dockerHelper; var _dockerComposeHelper; var _ptyProcess, _ptyProcessUser; var _vpnHelper; // Add npm path to home folder as services are otherwise not able to require them... // This should be added to start.js in mSB-node var corePath = path.resolve(settingsHelper.nodePackagePath, "node_modules"); require('app-module-path').addPath(corePath); require('module').globalPaths.push(corePath); require('module')._initPaths(); if (!settingsHelper.settings.policies) { settingsHelper.settings.policies = { "disconnectPolicy": { "heartbeatTimeout": 120, "missedHearbeatLimit": 3, "disconnectedAction": "RESTART", "reconnectedAction": "NOTHING", "offlineMode": true } }; settingsHelper.save(); } // Called by HUB if it was ot able to process the request /* istanbul ignore next */ function OnErrorMessage(message, code) { console.log('Error: '.red + message.red + " code: ".grey + code); if (code < 100) { // All exceptions less than 100 are serious and should reset settings let faultDescription = 'Node is being reset due to error code: ' + code + '. Message: ' + message; let faultCode = '00099'; trackException(faultCode, faultDescription); try { _client.invoke('traceLog', os.hostname(), `${faultDescription}. CODE:${faultCode}`); } catch (e) { } // Wait 5 min before trying again setTimeout(() => { OnReset(null); }, 5 * 60 * 1000); } if (code === 101) { // The hub is notifying the node that the session console.log('Restarting due to lost session.'); restart(); } } // Called by HUB when user clicks on the Hosts page function OnPing(id) { log("ping => " + microServiceBusNode.InboundServices().length + " active service(s)"); _client.invoke('pingResponse', settingsHelper.settings.nodeName, os.hostname(), "Online", id, false); } // Called by HUB to receive all active services /* istanbul ignore next */ function OnGetEndpoints(message) { console.log('OnGetEndpoints'.blue); } // Called by HUB when itineraries has been updated /* istanbul ignore next */ function OnUpdateItinerary(flowServiceUri) { if (typeof (flowServiceUri) === "string") { microServiceBusNode.UpdateItinerary(flowServiceUri); } else { microServiceBusNode.UpdateItinerary_Legacy(flowServiceUri); } microServiceBusNode.PersistEvent("Updated flows"); } // Called by HUB when itineraries has been updated function OnChangeState(state) { microServiceBusNode.ChangeState(state); } // Called by the HUB when disabling/enabling flow function OnUpdateFlowState(tineraryId, environment, enabled) { microServiceBusNode.UpdateFlowState(tineraryId, environment, enabled) .then(() => { microServiceBusNode.PersistEvent("Updated flow state"); }); } // Update debug mode function OnChangeDebug(debug) { microServiceBusNode.SetDebug(debug); _debugModeEnabled = debug ? Date.now() : Date.parse('2030-12-31'); microServiceBusNode.PersistEvent("Debug = " + debug); } // Update debug mode function OnRestartCom(callback) { microServiceBusNode.RestartCom(() => { callback(); }); } // Enable remote debugging /* istanbul ignore next */ function OnEnableDebug(connId) { log("CHANGING DEBUG MODE..."); microServiceBusNode.PersistEvent("Enabled remote debugging"); settingsHelper.settings.debugHost = connId; settingsHelper.save(); // Stop SignalR _client.stop() .then(() => { process.send({ cmd: 'START-DEBUG' }); }) .catch(() => { process.send({ cmd: 'START-DEBUG' }); }); setTimeout(function () { log('Restarting for debug'.yellow); // Kill the process process.exit(98); }, 1000); } function OnStopDebug() { log("CHANGING DEBUG MODE..."); microServiceBusNode.PersistEvent("Disaabled remote debugging"); settingsHelper.settings.debugHost = undefined; settingsHelper.save(); // Stop SignalR _client.stop() .then(() => { process.send({ cmd: 'STOP-DEBUG' }); }) .catch(() => { process.send({ cmd: 'STOP-DEBUG' }); }); setTimeout(function () { log('Restarting for debug'.red); // Kill the process process.exit(); }, 1000); } // Enable remote debugging function OnChangeTracking(enableTracking) { microServiceBusNode.SetTracking(enableTracking); microServiceBusNode.PersistEvent("Tracking = " + enableTracking); } // Incoming message from HUB function OnSendMessage(message, destination) { log(message.blue); } // Called by HUB when signin has been successful function OnSignInMessage(response) { log('Sign in complete...'.grey); response.basePath = __dirname; _signInState = "SIGNEDIN"; microServiceBusNode.SignInComplete(response); if (response.policies.proxyPolicy && response.policies.proxyPolicy.enableProxy) { const ProxyService = require('./services/proxyService.js'); self.proxyService = new ProxyService(log); self.proxyService.Start(response.policies.proxyPolicy) .then(() => { log('Proxy started'.grey); }) .catch(e => { log('Proxy failed to start'.red); }); } if (require("os").platform() === "linux" && response.policies.logPolicy && response.policies.logPolicy.transmitLogsAfterReboot) { util.getUptime() .then(seconds => { if (seconds < 200) { try { OnUploadSyslogs(null, null, response.policies.logPolicy.account, response.policies.logPolicy.accountKey) } catch (e) { log(`Unable to transmit log files. ${e}`.red); } } else { log(`System was rebooted ${(seconds / 60).toFixed(0)} minutes ago. No log files will be transmitted`.yellow); } }) .catch(e => { log("Unable to fetch system uptime. No logs will be transmitted".red); }) } log("Marking partition as good".yellow); let RaucHandler = require('./RaucHandler'); let raucHandler = new RaucHandler(); raucHandler.raucMarkPartition("good", "booted", function (err, slot, msg) { if (err) { log("Unable to mark partition as good. ".red + err); } else { log(`Successfully ${msg}`.green); } }); _debugModeEnabled = response.debug ? Date.now() : Date.parse('2030-12-31'); // Check if we're in debug mode if (process.execArgv.find(function (e) { return e.startsWith('--inspect'); }) !== undefined && !process.env.VSCODE_PID) { /* istanbul ignore next */ require('msb-network').get_active_interface(function (err, nw) { let maxWidth = 75; let debugUri = 'http://' + nw.ip_address + ':' + process.debugPort + '/json/list'; webRequest(debugUri, function (err, response, body) { if (response && response.statusCode === 200) { log(); log(util.padRight("", maxWidth, ' ').bgGreen.white.bold); log(util.padRight(" IN DEBUG", maxWidth, ' ').bgGreen.white.bold); log(util.padRight(" IP: " + nw.ip_address, maxWidth, ' ').bgGreen.white.bold); log(util.padRight(" PORT: " + process.debugPort, maxWidth, ' ').bgGreen.white.bold); let debugList = JSON.parse(body); _client.invoke( 'debugResponse', { debugHost: settingsHelper.settings.debugHost, organizationId: settingsHelper.settings.organizationId, list: debugList } ); } else { log('Unable to receive debug data.'.red); } }); }); } log("Check for WireGuard installation"); exec("wg", function (error, stdout, stderr) { if (!error) { log(`Requesting VPN update`); _client.invoke('getVpnSettings', null); } else { log(`VPN error: ${error}`); } }); } // Called by HUB when node has been successfully created /* istanbul ignore next */ function OnNodeCreated(nodeData) { nodeData.machineName = os.hostname(); settingsHelper.settings = extend(settingsHelper.settings, nodeData); log('Successfully created node: ' + nodeData.nodeName.green); settingsHelper.save(); microServiceBusNode.settingsHelper = settingsHelper; microServiceBusNode.NodeCreated(); _client.invoke('created', nodeData.id, settingsHelper.settings.nodeName, os.hostname(), "Online", nodeData.debug, pjson.version, settingsHelper.settings.organizationId); microServiceBusNode.PersistEvent("Node created"); } // Called when the hub require state information (network, storage, memory and cpu) function OnReportState(id, debugCallback) { let network = require('msb-network'); _client.invoke('notify', id, `Fetching environment state from ${settingsHelper.settings.nodeName}.`, "INFO"); network.get_interfaces_list(function (err, nw) { let path = os.platform() === 'win32' ? 'c:' : '/'; var state = { networks: nw, memory: { totalMem: (os.totalmem() / 1000 / 1000).toFixed(2) + ' Mb', freemem: (os.freemem() / 1000 / 1000).toFixed(2) + ' Mb' }, cpus: os.cpus(), os: os, env: process.env, devs: ['darwin', 'linux'].includes(os.platform()) ? fs.readdirSync('/dev') : [] }; util.getAvailableDiskspace(function (err, storeageState) { if (!err) { state.storage = storeageState; } if (debugCallback) { debugCallback(true); } else { /* istanbul ignore next */ if (process.env.SNAP) { let SnapHelper = require('./SnapHelper') let snapHelper = new SnapHelper() snapHelper.on('log', (msg) => { log(msg); }); snapHelper.listSnaps() .then((list) => { log("mapping snaps") state.snapList = list.map((snap => { return { Name: snap.name, Version: snap.version, Rev: snap.revision, Tracking: snap["tracking-channel"], Publisher: snap.publisher ? snap.publisher["display-name"] : "-", Notes: snap.summary } })); log("REPORTED STATE - with snap") _client.invoke('reportStateResponse', state, id); }) .catch(e => { log(`Error ${e}`); }); } /* istanbul ignore next */ else if (process.env.MSB_PLATFORM === "YOCTO" || process.env.MSB_PLATFORM === "AZUREIOTEDGE") { let RaucHandler = require('./RaucHandler'); let raucHandler = new RaucHandler(); raucHandler.raucGetSlotStatus(function (err, raucState) { if (!err) { let arrayToObject = (array) => array.reduce((obj, item) => { obj[item.key] = item.val return obj }, {}) state.raucState = { rootfs0: arrayToObject(raucState.rootfs0), rootfs1: arrayToObject(raucState.rootfs1) }; } log("REPORTED STATE - with yocto") _client.invoke('reportStateResponse', state, id); }); } else { log("REPORTED STATE - no snap or firmware info") _client.invoke('reportStateResponse', state, id); } } }); }); microServiceBusNode.PersistEvent("Required State"); } // Update snap /* istanbul ignore next */ function OnRefreshSnap(snap, mode, connid) { mode = snap.indexOf("microservicebus") === 0 ? "devmode" : mode; if (connid) { _client.invoke('notify', connid, `${settingsHelper.settings.nodeName} - Refreshing ${snap} (${mode})`, "INFO"); } util.refreshSnap(snap, mode, function (err, output) { let msg = `${settingsHelper.settings.nodeName} - ${snap} is refreshed`; if (err) { msg = `${settingsHelper.settings.nodeName} - Unable to refresh ${snap}. Error: ${err}`; } _client.invoke('notify', connid, msg, "INFO"); console.log(msg); console.log(output); }); } // Called by HUB when node is to be resetted /* istanbul ignore next */ function OnReset(id) { log("RESETTING NODE".bgRed.white); microServiceBusNode.PersistEvent("Node is being reset"); var isRunningAsSnap = settingsHelper.isRunningAsSnap; let msbHubUri = settingsHelper.settings.hubUri; settingsHelper.settings = { "debug": false, "hubUri": msbHubUri, "useEncryption": false }; settingsHelper.save(); _client.invoke('notify', id, `Node ${settingsHelper.settings.nodeName} has been reset`, "INFO"); // Stop all services microServiceBusNode.ForceStop(function (callback) { // Restart setTimeout(function () { if (isRunningAsSnap) restart(); else process.exit(); }, 3000); }); } // Called by HUB when node is to be resetted /* istanbul ignore next */ function OnResetKeepEnvironment(id) { log("RESETTING NODE".bgRed.white); microServiceBusNode.PersistEvent("Node is being reset"); var isRunningAsSnap = settingsHelper.isRunningAsSnap; let msbHubUri = settingsHelper.settings.hubUri; settingsHelper.settings = { "debug": false, "hubUri": msbHubUri, "useEncryption": false }; settingsHelper.save(); _client.invoke('notify', id, `Node ${settingsHelper.settings.nodeName} has been reset`, "INFO"); // Stop all services microServiceBusNode.ForceStop(function (callback) { // Restart setTimeout(function () { if (isRunningAsSnap) restart(); else process.exit(); }, 3000); }); } // Calling for syslogs from the portal // Logs are extracted and pushed to blob storage /* istanbul ignore next */ function OnUploadSyslogs(connectionId, f, account, accountKey, debugCallback) { if (os.platform() === 'win32') { _client.invoke('notify', connectionId, `${settingsHelper.settings.nodeName} is running on Windows. The Upload Syslog feature is not implemented on Windows.`, "ERROR"); return; } log("Requesting syslogs"); const tmpPath = "/tmp/"; // MUST end with / 'slash' const logPath = "/var/log/"; // MUST end with / 'slash' const filePattern = /^syslog.*|^messages.*|^system.*|^journallog.*/; let fileNamePrefix = settingsHelper.settings.organizationId + "-" + settingsHelper.settings.nodeName + "-" + new Date().toISOString().replace(/[:\.\-T]/g, '') + "-"; util.addNpmPackage('azure-storage', function (err) { if (err) { log("Unable to add azure-storage package." + err); if (debugCallback) { debugCallback(false); } else { _client.invoke('notify', connectionId, "Unable to add azure-storage package." + err, "ERROR"); } return; } else { log("azure-storage package installed."); } fs.readdir(logPath, function (err, items) { if (err) { log(`ERROR: ${JSON.stringify(err)}`); throw `${JSON.stringify(err)}`; } else if (items.length === 0) { log("No files found"); throw "No files found"; } else { for (var i = 0; i < items.length; i++) { // ignore non interesting log files. if (!items[i].match(filePattern)) continue; log("Found " + items[i]); var originalFilePath = path.resolve(logPath, items[i]); let newFilePath = path.resolve(tmpPath, fileNamePrefix + items[i] + ".gz"); util.compress(originalFilePath, newFilePath, function (err, fileName) { if (err) { log("Unable to create syslog file. " + err); if (debugCallback) { debugCallback(false); } else { _client.invoke('notify', connectionId, "Unable to create syslog file. " + err, "ERROR"); } } else { log("Compressed " + originalFilePath + " to " + fileName); util.sendToBlobStorage(account, accountKey, settingsHelper.settings.organizationId, settingsHelper.settings.nodeName, fileName, "syslogs", function (err) { log("Sent " + fileName + " to blobstorage"); }); } }); } } }); util.extractJournal(tmpPath, account, accountKey, settingsHelper.settings.organizationId, settingsHelper.settings.nodeName, log) .then(function () { log("Sent all journallogs"); if (connectionId) { _client.invoke('notify', connectionId, "Logfiles sent to blobstorage", "INFO"); } }).catch(function (e) { log("Error uploading syslogs " + e); if (connectionId) { _client.invoke('notify', connectionId, "Error uploading syslogs " + e, "ERROR"); } }); }); } // Called from portal to resubmit messages function OnResendHistory(req) { microServiceBusNode.ResendHistory(new Date(req.startdate), new Date(req.enddate)); microServiceBusNode.PersistEvent("Resent history"); } // Submitting events and alters to the portal function OnRequestHistory(req) { microServiceBusNode.RequestHistory(new Date(req.startdate).getTime(), new Date(req.enddate).getTime(), req.connId); } // Resetting the host in the settings.json and restarting the node function OnTransferToPrivate(req) { microServiceBusNode.PersistEvent("Node Transfered"); settingsHelper.settings = { "debug": false, "hubUri": req.hubUri, "useEncryption": false }; settingsHelper.save(); restart(); } // Triggered when token is updated function OnUpdatedToken(token) { microServiceBusNode.UpdateToken(token); log("Token has been updated"); microServiceBusNode.PersistEvent("Updated token"); // TODO! // Sign in again } // Triggered from portal to update Yocto firmware /* istanbul ignore next */ function OnUpdateFirmware(force, connid, requiredPlatform, requiredVersion, requestId) { let RaucHandler = require('./RaucHandler'); let raucHandler = new RaucHandler(); raucHandler.on('progress', function (progressInfo) { log(progressInfo); }); raucHandler.raucGetSlotStatus(function (err, platformStatus) { if (err) { log("Unable to update firmware".red + err); } else { let rootfs0_status = platformStatus.rootfs0.find(function (p) { return p.key === 'state'; }); let currentPlatform = rootfs0_status.val === "booted" ? platformStatus.rootfs0 : platformStatus.rootfs1; let platform = platformStatus.platform; let version = currentPlatform.find(function (p) { return p.key === 'bundle.version'; }); let bootStatus = currentPlatform.find(function (p) { return p.key === 'boot-status'; }); let installed = currentPlatform.find(function (p) { return p.key === 'installed.timestamp'; }); if (requiredPlatform && requiredPlatform != platform) { log("Ignoring update for different platform"); return; } if (!version) { version = { val: "0.0.0" }; } if (!bootStatus) { bootStatus = { val: "FIRST INSTALL" }; } if (!installed) { installed = { val: "FIRST INSTALL" }; } let uri = `${settingsHelper.settings.hubUri}/api/nodeimages/${settingsHelper.settings.organizationId}/${(requiredPlatform ? requiredPlatform : platform)}${(requiredVersion ? "?version=" + requiredVersion : "")}`; uri = uri.replace('wss://', 'https://'); log("Notified on new firmware".yellow); log("Current firmware platform: ".yellow + platform.grey); log("Current firmware version: ".yellow + version.val.grey); log("Current boot status: ".yellow + bootStatus.val.grey); log("Current firmware installed: ".yellow + installed.val.grey); log("Fetching meta data from: ".yellow + uri.grey); // Download the meta data webRequest(uri, function (err, response, data) { if (response.statusCode != 200 || err != null) { log("No firmware image found".red); return; } else { let metamodel = JSON.parse(data); if (force || util.compareVersion(metamodel.version, version.val)) { if (settingsHelper.settings.state === "Active") { microServiceBusNode.ChangeState("InActive"); } log("New firmware version".yellow); let dir = path.resolve(settingsHelper.homeDirectory, "firmwareimages"); if (!fs.existsSync(dir)) { log("Creating firmwareimages directory".yellow); fs.mkdirSync(dir); } else { fs.readdirSync(dir).forEach(function (file, index) { log("Removing existing file".yellow); var curPath = path.resolve(dir, file); fs.unlinkSync(curPath); }); } var fileName = path.resolve(dir, path.basename(metamodel.uri)); var file = fs.createWriteStream(fileName); var https = require('https'); log("Downloading image from ".yellow + metamodel.uri.grey); let options = { timeout: 1000 * 60 * 10, //10 min timeout }; var request = https.get(metamodel.uri, options, function (response) { response.pipe(file); file.on('finish', function () { file.close(function () { log("Download complete".yellow); log("Calling RAUC".yellow); log("Installing ".yellow + fileName.grey); if (connid) { _client.invoke('notify', connid, "Download complete, installation initiated on " + settingsHelper.settings.nodeName, "INFO"); } raucHandler.raucInstall(fileName, function (err) { if (err) { log("Unable to install RAUC image. ".red + err); if (connid) _client.invoke('notify', connid, "Unable to install RAUC image on " + settingsHelper.settings.nodeName, "INFO"); } else { log("Successfully installed RAUC image.".green); log("removing node-pty".green); try { fs.rmSync(`${process.env["SNAP_USER_DATA"]}/node_modules/node-pty`, { recursive: true, force: true }); } catch (e) { log(`Unable to remove node-pty: ${e}`.red); }; if (connid) { _client.invoke('notify', connid, "Successfully installed RAUC image on " + settingsHelper.settings.nodeName + ". Node is now rebooting", "INFO"); } if (requestId) { var reportState = { reported: { msbFirmwareUpdate: { requestId: requestId } } }; microServiceBusNode.UpdateComState(reportState) .then(() => { log("Requested firmware state has been updated".green); setTimeout(function () { util.reboot(); }, 10000); }) .catch(e => { log("Unable to persist requested firmware state. This might set the Node into a reboot loop!".red + ` Error: ${e}`); }); } else { setTimeout(function () { util.reboot(); }, 10000); } } }); }); }); }).on('error', function (err) { // Handle errors fs.unlink(fileName); // Delete the file async. (But we don't check the result) log("unable to download firmware".red); }); } else { log("Already running latest version".yellow); } } }); } }); } /* istanbul ignore next */ function OnSetBootPartition(partition, connid) { log("Marking partition ".yellow + partition); let RaucHandler = require('./RaucHandler'); let raucHandler = new RaucHandler(); raucHandler.raucMarkPartition("active", partition, async function (err, slot, msg) { if (err) { log("Unable to mark partition".red + err); } else { log(`Removing node-pty`.green); try { fs.rmSync(`${process.env["SNAP_USER_DATA"]}/node_modules/node-pty`, { recursive: true, force: true }); } catch (e) { log(`Unable to remove node-pty: ${e}`.red); } log(`Successfully ${msg}. Rebooting...`.green); _client.invoke('notify', connid, "Successfully marked partition. Rebooting " + settingsHelper.settings.nodeName, "INFO"); setTimeout(function () { util.reboot(); }, 2000); } }); } // Call mSB-dam to update grants /* istanbul ignore next */ function OnGrantAccess() { log("Grant Access called"); //Load DAM dependency let MicroServiceBusDAM = require('./MicroServiceBusDAM.js'); microServiceBusDAM = new MicroServiceBusDAM(); //refresh grants microServiceBusDAM.refresh(function (err, response) { if (err) { log("Grants was not updated due to error: ".red + err); } else { log("Grants was updated".green); } }); } // Execute tests script /* istanbul ignore next */ function OnRunTest(testDescription) { log("Run test called"); microServiceBusNode.RunTest(testDescription); } function OnPingNodeTest(connid) { _client.invoke('pingNodeTestResponse', { nodeId: settingsHelper.settings.id, connId: connid }); } function OnUpdatePolicies(policies) { settingsHelper.settings.policies = policies; settingsHelper.save(); log("Policies has been updated. Restarting".green); _client.invoke('logMessage', settingsHelper.settings.nodeName, `Polycies for ${settingsHelper.settings.nodeName} has been updated.`, settingsHelper.settings.organizationId); setTimeout(restart, 2000); } function OnExecuteScript(patchScript, parametes, connid) { let localPatchUri = settingsHelper.settings.hubUri + '/api/Scripts/' + patchScript; localPatchUri = localPatchUri.replace('wss://', 'https://'); let requestOptions = { maxAttempts: 2, retryDelay: 3000, url: localPatchUri, }; log(`Downloading script: ${localPatchUri}`); webRequest(requestOptions, function (err, response, scriptContent) { if (response.statusCode != 200 || err != null) { if (connid) _client.invoke('notify', connid, `Unable to download ${localPatchUri} for ${settingsHelper.settings.nodeName}.`, "INFO"); } else { let fileName = path.basename(patchScript); let localPatchScript = path.resolve(settingsHelper.serviceDirectory, fileName); scriptContent = scriptContent.replace("\r\n", "\n"); fs.writeFileSync(localPatchScript, scriptContent); log("Executing script"); util.executeScript(`${localPatchScript} ${parametes}`, function (err) { log(`Done executing script. Errors: ${err}`); if (connid) _client.invoke('notify', connid, `${fileName} execute successfully on ${settingsHelper.settings.nodeName}.`, "INFO"); }); } }); } function OnUpdateVulnerabilities(connid) { log('Vulnerabilities scan initiated'.gray); doVulnerabilitiesScan(); _client.invoke('notify', connid, `Vulnerabilities scan initiated on ${settingsHelper.settings.nodeName}.`, "INFO"); } // Docker operations function OnDockerListImages(request, connid) { initDockerHelper(); _dockerHelper.listImages() .then(result => { log(`${arr.length} images installed before installation`); _client.invoke('dockerListImagesResponse', result, connid); }) .catch(e => { log(`Error ${e}`) }); }; function OnDockerListContainers(request, connid) { initDockerHelper(); _dockerHelper.listContainers(request.all) .then(result => { log(`${result.length} containers in total...`); log(JSON.stringify(result)); _client.invoke('dockerListContainersResponse', result, connid); }) .catch(e => { log(`Error ${e}`) }); }; function OnDockerInstallImage(request, connid) { initDockerHelper(); let createImageRequest = { fromImage: request.fromImage } _client.invoke('dockerInstallImageResponse', `Starting to install ${request.fromImage}image`, connid); _dockerHelper.createImage(createImageRequest) .then(async result => { await _dockerHelper.wait(8000); let createContainerRequest = { Image: request.image, name: request.name } _client.invoke('dockerInstallImageResponse', `${request.fromImage} installed... starting up`, connid); _dockerHelper.createContainer(createContainerRequest) .then(async () => { await _dockerHelper.wait(2000); _dockerHelper.startContainer(`/${createContainerRequest.name}`) .then(async () => { _client.invoke('dockerInstallImageResponse', `${request.fromImage} installed and started successfully`, connid); await _dockerHelper.wait(4000); _dockerHelper.listContainers(true) .then(result => { _client.invoke('dockerListContainersResponse', result, connid); }) .catch(e => { log(`Error ${e}`) }); }) .catch(e => { _client.invoke('dockerDeleteImageResponse', `Failed to start ${createContainerRequest.name}: ${e}`, connid); console.log(`Error ${e} - ${createContainerRequest.name}`) }); }) .catch(e => { _client.invoke('dockerDeleteImageResponse', `Failed to create ${createContainerRequest.name}: ${e}`, connid); console.log(`Error ${e}`) }); }) .catch(e => { _client.invoke('dockerDeleteImageResponse', `Failed to install image ${request.fromImage}: ${e}`, connid); log(`Error ${e}`) }); }; function OnDockerDeleteImage(request, connid) { initDockerHelper(); _dockerHelper.deleteContainer(request.name) .then(async () => { if (request.tryDeleteImage) { await _dockerHelper.wait(2000); _dockerHelper.deleteImage(container.data.Image) .then(async () => { _client.invoke('dockerDeleteImageResponse', `${request.name} deleted successfully`, connid); await _dockerHelper.wait(2000); _dockerHelper.listContainers(true) .then(result => { _client.invoke('dockerListContainersResponse', result, connid); }) .catch(e => { _client.invoke('dockerDeleteImageResponse', `Failed to delete ${request.name}: ${e}`, connid); log(`Error ${e}`) }); }) .catch(e => { _client.invoke('dockerDeleteImageResponse', `Failed to delete ${request.name}: ${e}`, connid); console.log(`Error ${e}`) }); } else { _client.invoke('dockerDeleteImageResponse', `${request.name} deleted successfully`, connid); await _dockerHelper.wait(2000); _dockerHelper.listContainers(true) .then(result => { _client.invoke('dockerListContainersResponse', result, connid); }) .catch(e => { log(`Error ${e}`) }); } }) .catch(e => { _client.invoke('dockerDeleteImageResponse', `Failed to delete ${request.name}: ${e}`, connid); console.log(`Error ${e}`) }); }; function OnDockerStartContainer(request, connid) { initDockerHelper(); log(`Starting container ${request.name}`); _dockerHelper.startContainer(request.name) .then(async result => { _client.invoke('dockerStartContainerResponse', `${request.name} started successfully`, connid); await _dockerHelper.wait(1000); _dockerHelper.listContainers(true) .then(result => { _client.invoke('dockerListContainersResponse', result, connid); }) .catch(e => { log(`Error ${e}`) }); }) .catch(e => { console.log(`Error ${e}`) }); }; function OnDockerStopContainer(request, connid) { log(`Stopping container`); _dockerHelper.stopContainer(request.name) .then(async result => { _client.invoke('dockerStopContainerResponse', `${request.name} stopped successfully`, connid); await _dockerHelper.wait(1000); _dockerHelper.listContainers(true) .then(result => { _client.invoke('dockerListContainersResponse', result, connid); }) .catch(e => { log(`Error ${e}`) }); }) .catch(e => { console.log(`Error ${e}`) }); }; function OnDockerComposeList(connid) { _client.invoke('notify', connid, `docker-compose list called on ${settingsHelper.settings.nodeName}.`, "INFO"); initDockerContainerHelper() .then(() => { _dockerComposeHelper.list() .then(async result => { _client.invoke('dockerComposelistResponse', result, connid); }) .catch(error => { log(`Error ${error}`); _client.invoke('notify', connid, `(OnDockerComposeList) ${error}`, "ERROR"); }); }) .catch(error => { log(e); _client.invoke('notify', connid, `(OnDockerComposeList) ${error}`, "ERROR"); }); } function OnDockerComposeInstall(request, connid) { _client.invoke('notify', connid, `docker-compose install called on ${settingsHelper.settings.nodeName}.`, "INFO"); initDockerContainerHelper() .then(async () => { try { const fileName = "docker-compose.yaml" // Create directory const dockerComposePath = path.resolve(settingsHelper.homeDirectory, "docker-compose"); if (!fs.existsSync(dockerComposePath)) { fs.mkdirSync(dockerComposePath); } const dockerComposeService = request.name.replace(/\.[^/.]+$/, ""); const dockerComposeServicePath = path.resolve(dockerComposePath, dockerComposeService.toLowerCase()); if (!fs.existsSync(dockerComposeServicePath)) { fs.mkdirSync(dockerComposeServicePath); } // Download docker-compose file await util.downloadFile(dockerComposeServicePath, fileName, `${settingsHelper.settings.hubUri}/api/scripts${request.url}`); // Download dependancy files for (let index = 0; index < request.dependancies.length; index++) { const dependancy = request.dependancies[index]; await util.downloadFile(dockerComposeServicePath, dependancy.name, `${settingsHelper.settings.hubUri}/api/scripts${dependancy.url}`); } await _dockerComposeHelper.build(dockerComposeServicePath); await _dockerComposeHelper.up(dockerComposeServicePath); const list = await _dockerComposeHelper.list(); _client.invoke('dockerComposelistResponse', list, connid); } catch (error) { log(error); _client.invoke('notify', connid, `(OnDockerComposeList) ${error}`, "ERROR"); } }) .catch(error => { log(error); _client.invoke('notify', connid, `(OnDockerComposeList) ${error}`, "ERROR"); }); } function OnDockerComposeDown(config, connid) { _client.invoke('notify', connid, `docker-compose down called on ${settingsHelper.settings.nodeName}.`, "INFO"); initDockerContainerHelper() .then(async () => { // Create directory try { const dockerComposePath = path.resolve(settingsHelper.homeDirectory, "docker-compose"); const dockerComposeServicePath = path.resolve(dockerComposePath, config.toLowerCase()); await _dockerComposeHelper.down(dockerComposeServicePath); const list = await _dockerComposeHelper.list(); _client.invoke('dockerComposelistResponse', list, connid); } catch (error) { log(error); _client.invoke('notify', connid, `(OnDockerComposeList) ${error}`, "ERROR"); } }) .catch(error => { log(error); _client.invok