// Module responsible for updating the information about the different machines known to the MS
import {
statusEndpoint,
machineEndpoint,
processEndpoint,
} from '../ms-engine-communication/module.js';
import logger from '../../logging.js';
import helpers from '../../../../shared-frontend-backend/helpers/javascriptHelpers.js';
const { deepEquals } = helpers;
/**
* Sends request checking if there is an engine running on the given machine
*
* @param {object} machine
* @param {string} machine.ip the ip of the given machine
* @param {number} machine.port the port the engine is accessible through
* @returns {Promise<boolean>} indicates if the machine is available or not
*/
export async function checkAvailability(machine) {
// check if machine is reachable and running an engine; enforce usage of node http to avoid error in V8
const available = await statusEndpoint.getStatus(machine);
return available;
}
/**
* Checks if there are deployments on the machine that are not known locally or vice versa and updates accordingly
*/
async function getUpdatedDeployments(machine, deployedProcesses, deploymentsOnMachine) {
// check if there were changes in which processes are deployed to this machine
const storedDeployments = Object.values(deployedProcesses).map(
(deployment) => deployment.definitionId
);
const newDeploymentFound = deploymentsOnMachine.some(
(deployment) => !storedDeployments.includes(deployment.definitionId)
);
const deploymentRemoved = storedDeployments.some(
(definitionId) =>
!deploymentsOnMachine.some((deployment) => deployment.definitionId === definitionId)
);
if (newDeploymentFound || deploymentRemoved) {
deployedProcesses = deploymentsOnMachine.map((deploymentFromMachine) => {
const deployment = Object.values(deployedProcesses).find(
(process) => process.definitionId === deploymentFromMachine.definitionId
);
// if we already know this deployment => just use the already existing information
if (deployment) {
return deployment;
}
return {
definitionId: deploymentFromMachine.definitionId,
instances: [],
bpmn: deploymentFromMachine.bpmn,
name: deploymentFromMachine.definitionName,
deploymentDate: deploymentFromMachine.deploymentDate,
deploymentMethod: deploymentFromMachine.deploymentMethod,
};
});
// create object with key: id of deplyed process, value: information about deployment
deployedProcesses = deployedProcesses.reduce(
(deployed, deployment) => ({
...deployed,
[deployment.definitionId]: deployment,
}),
{}
);
return deployedProcesses;
}
return;
}
/**
* Gets updated information about the processes deployed on the given machine
*
* @param {object} machine
* @param {string} machine.ip the ip of the given machine
* @param {number} machine.port the port the engine is accessible through
* @returns {object|undefined} information about the processes deployed on the machine or undefined if nothing changed
*/
export async function getUpdatedDeploymentInfo(machine) {
let deployedProcesses;
let updated = false;
if (!machine.deployedProcesses) {
deployedProcesses = {};
updated = true;
} else {
deployedProcesses = JSON.parse(JSON.stringify(machine.deployedProcesses));
}
const { name, hostname, id } = machine;
logger.debug(`Sending requests for up to date deployment info to ${name || hostname || id}.`);
// get information about all processes deployed to the current machine
const deploymentsOnMachine = await processEndpoint.getDeployedProcesses(machine);
const updatedDeployments = await getUpdatedDeployments(
machine,
deployedProcesses,
deploymentsOnMachine
);
// deployments didn't change from last time they were checked
if (updatedDeployments) {
updated = true;
deployedProcesses = updatedDeployments;
}
// simultaniously request instance information for all instances
const deploymentInfoRequests = deploymentsOnMachine.map(async (deployment) => {
const instanceIds = await processEndpoint.getProcessInstances(machine, deployment.definitionId);
const instanceRequests = instanceIds.map(
async (id) =>
await processEndpoint.getInstanceInformation(machine, deployment.definitionId, id)
);
const instances = await Promise.all(instanceRequests);
return {
definitionId: deployment.definitionId,
instances,
};
});
const deploymentInfo = await Promise.all(deploymentInfoRequests);
// update instance information if there are new instances or some instance changed state
deploymentInfo.forEach((info) => {
info.instances.forEach((instance) => {
const deployment = Object.values(deployedProcesses).find(
(process) => process.definitionId === info.definitionId
);
const storedInstanceIndex = deployment.instances.findIndex(
(knownInstance) => instance.processInstanceId === knownInstance.processInstanceId
);
if (storedInstanceIndex < 0) {
deployment.instances.push(instance);
updated = true;
} else {
// check deep equality and only overwrite when something actually changed
if (!deepEquals(deployment.instances[storedInstanceIndex], instance)) {
deployment.instances[storedInstanceIndex] = instance;
updated = true;
}
}
});
});
if (updated) {
return deployedProcesses;
}
return undefined;
}
/**
* Returns the updated information for a machine if the machine information changed (returns undefined if not)
*
* @param {Object} machine the machine we want to request information for
* @returns {Object|undefined} the updated object or undefined if there was no new information
*/
export async function getCompleteMachineInformation(machine) {
let updated = false;
let updatedMachine = { ...machine };
const { name, hostname, ip } = machine;
logger.debug(`Sending GET request to ${name || hostname || ip} to get machine info!`);
const properties = await machineEndpoint.getProperties(machine);
//delete some values that change to often to be stored (or just don't make sense to store)
const { cores, physicalCores, processors, speed } = properties.cpu;
properties.cpu = { cores, physicalCores, processors, speed };
properties.mem = { total: properties.mem.total };
properties.disk = properties.disk.map(({ type, total }) => ({ type, total }));
properties.battery = { hasBattery: properties.battery.hasBattery };
const metainfo = ['id', 'hostname', 'name', 'description'];
Object.entries(properties).forEach(([propertyName, value]) => {
if (value !== machine.machine[propertyName]) {
updatedMachine.machine[propertyName] = value;
updated = true;
}
if (metainfo.includes(propertyName)) {
if (value !== updatedMachine[propertyName]) {
updatedMachine[propertyName] = value;
updated = true;
}
}
});
const deploymentInfo = await getUpdatedDeploymentInfo(machine);
if (deploymentInfo) {
updatedMachine.deployedProcesses = deploymentInfo;
updated = true;
}
if (updated) {
return updatedMachine;
}
}