Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Control/lib/kafka/adapters/odc/odcDeviceEventAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
const { OdcDeviceInfoAdapter } = require('../../../adapters/OdcDeviceInfoAdapter.js');

/**
* @typedef {Object} deviceStateChanged
* @typedef {OdcDeviceInfo} deviceStateChanged
*
*
* @example
Expand Down
1 change: 1 addition & 0 deletions Control/lib/services/Environment.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class EnvironmentService {
environmentInfo.events = [...cachedEnvironment.events];
environmentInfo.isDeploying = cachedEnvironment.isDeploying;
environmentInfo.deploymentError = cachedEnvironment.deploymentError;
environmentInfo.firstTaskInError = cachedEnvironment.firstTaskInError;
}
return environmentInfo;
}
Expand Down
17 changes: 13 additions & 4 deletions Control/lib/services/environment/EnvironmentCache.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,27 @@ class EnvironmentCacheService {
* * Heartbeat calls (GetEnvironment/GetEnvironments) - which will NOT contain `isDeploying` and `deploymentError` properties
* * Cache caught events - which should contain `isDeploying` and `deploymentError` properties
* @param {string} id - the id of the environment to be updated
* @param {EnvironmentInfo} environment - the new environment information to be set
* @param {Partial<EnvironmentInfo>} environment - the new environment information to be set
* @returns {void}
*/
addOrUpdateEnvironment(environment, shouldBroadcast = false) {
const { id } = environment;
if (this._environments.has(id)) {
const cachedEnvironment = this._environments.get(id);
const { events = [] } = cachedEnvironment;
const {isDeploying, deploymentError } = cachedEnvironment;
/**
* @param {EnvironmentInfo} cachedEnvironment - the environment information currently stored in cache for the environment with the given id
* @param {boolean} cachedEnvironment.isDeploying - the information if the environment is being deployed
* @param {string} cachedEnvironment.deploymentError - the error message if the environment deployment failed
* @param {TaskEvent|OdcDeviceInfoEvent} cachedEnvironment.firstTaskInError - the first task event in error for the environment, which can be either a FLP task or an ODC device state change
*/
const { isDeploying, deploymentError, firstTaskInError } = cachedEnvironment;
const updatedEnvironment = Object.assign({}, cachedEnvironment, environment);
updatedEnvironment.events = [...events];
updatedEnvironment.isDeploying = isDeploying;
updatedEnvironment.deploymentError = deploymentError;
updatedEnvironment.firstTaskInError = firstTaskInError;

this._environments.set(id, updatedEnvironment);
} else {
this._environments.set(id, { ...environment, events: environment.events ?? [] });
Expand Down Expand Up @@ -195,7 +203,7 @@ class EnvironmentCacheService {
*/
_handleFirstTaskInError(environmentId, event) {
if (
(event.state === TaskState.ERROR || event.state === TaskState.ERROR_CRITICAL)
(event.state === TaskState.ERROR_CRITICAL)
&& this._environments.has(environmentId)
&& !this._environments.get(environmentId).firstTaskInError
) {
Expand Down Expand Up @@ -236,6 +244,7 @@ class EnvironmentCacheService {

if (
state === EnvironmentState.CONFIGURED &&
transition?.name === EnvironmentTransitionType.CONFIGURE &&
transition?.status === EcsOperationAndStepStatus.DONE_OK
) {
// Once the environment is configured and ongoing transition is done, we can set the isDeploying to false
Expand All @@ -253,9 +262,9 @@ class EnvironmentCacheService {
this.addOrUpdateEnvironment(cachedEnvironment, false);

if (
state === EnvironmentState.DONE &&
transition?.name === EnvironmentTransitionType.DESTROY &&
transition?.status === EcsOperationAndStepStatus.DONE_OK &&
state === EnvironmentState.DONE &&
!cachedEnvironment.deploymentError
) {
// That is, if the environment successfully ended the DESTROY transition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const UNKNOWN = 'UNKNOWN';
export const environmentComponentsSummary = (environmentInfo) => {
const odcState = environmentInfo?.hardware?.epn?.info?.state ?? UNKNOWN;
const ddsState = environmentInfo?.hardware?.epn?.info?.ddsSessionStatus ?? UNKNOWN;
const { currentTransition = undefined, state } = environmentInfo;
const { currentTransition = undefined, state, firstTaskInError } = environmentInfo;

const odcStateStyle = ODC_STATE_COLOR[odcState] ? `.${ODC_STATE_COLOR[odcState]}` : '';
const ddsStateStyle = ODC_STATE_COLOR[ddsState] ? `.${ODC_STATE_COLOR[ddsState]}` : '';
Expand All @@ -39,11 +39,16 @@ export const environmentComponentsSummary = (environmentInfo) => {
? `.${ALIECS_TRANSITION_COLOR[currentTransition] ? ALIECS_TRANSITION_COLOR[currentTransition] : ''}`
: `.${ALIECS_STATE_COLOR[state] ? ALIECS_STATE_COLOR[state] : ''}`,
};
return miniCard(_getTitle(currentTransition), [
h('.flex-column', [
h(`${ecsData.style}`, ecsData.info),
h(`${odcStateStyle}`, 'ODC state: ', odcState),
h(`${ddsStateStyle}`, 'DDS state: ', ddsState),
return h('.flex-row.g2', [
miniCard(_getTitle(currentTransition), [
h('.flex-column', [
h(`${ecsData.style}`, ecsData.info),
h(`${odcStateStyle}`, 'ODC state: ', odcState),
h(`${ddsStateStyle}`, 'DDS state: ', ddsState),
]),
]),
firstTaskInError && miniCard('First Task In Error', [
_firstTaskInErrorDisplay(firstTaskInError)
]),
]);
};
Expand All @@ -64,3 +69,50 @@ const _getTitle = (currentTransition) =>
h('h5.flex-column.flex-center', 'Components State')
]
);

/**
* @private
* Method to get the first task in error display, it checks if the event is an ODC device event or a ECS task event and creates the display accordingly
* @param {TaskEvent | OdcDeviceInfoEvent} taskEvent - the task event with error information
* @returns {vnode} - display of the task event in case of error
*/
const _firstTaskInErrorDisplay = (taskEvent) => {
if (taskEvent?.partitionId) {
return _odcDeviceEventInErrorDisplay(taskEvent);
} else {
return _ecsTaskEventInErrorDisplay(taskEvent);
}
};

/**
* @private
* Method to create the display of the task event in case of error
* @param {TaskEvent} taskEvent - the task event with error information
* @returns {vnode} - display of the task event in case of error
*/
const _ecsTaskEventInErrorDisplay = (taskEvent = {}) => {
const { name, hostname, id, status, isCritical } = taskEvent;
return h(`.flex-column${isCritical ? '.danger' : '.warning'}`, [
h('span', `ID: ${id}`),
h('span', `Name: ${name}`),
h('span', `Host: ${hostname}`),
h('span', `Status: ${status}`),
]);
};

/**
* @private
* Method to create the display of the ODC device event in case of error
* @param {OdcDeviceInfoEvent} odcDeviceEvent - the ODC device event with error information
* @returns {vnode} - display of the ODC device event in case of error
*/
const _odcDeviceEventInErrorDisplay = (odcDeviceEvent = {}) => {
const { taskId, hostname, state, path, error } = odcDeviceEvent;
return h('.flex-column', [
h('span', `ID: ${taskId}`),
h('span', `Host: ${hostname}`),
h('span', `State: ${state}`),
h('span', `Path: ${path}`),
h('.danger', `Error: ${error}`)
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ describe(`'EnvironmentCacheService' - test suite`, () => {
isDeploying: undefined,
deploymentError: undefined,
state: 'inactive',
events: []
events: [],
firstTaskInError: undefined,
});
assert.strictEqual(broadcastServiceMock.broadcast.callCount, 1);
});
Expand All @@ -88,6 +89,47 @@ describe(`'EnvironmentCacheService' - test suite`, () => {

assert.ok(environmentCacheService._lastUpdate >= beforeUpdate);
});

it('should preserve the `firstTaskInError` field when updating an existing environment', () => {
const firstTaskInError = {
environmentId: 'env123',
state: 'ERROR',
taskid: 1,
name: 'task1',
hostname: 'host1',
className: 'class1',
isCritical: false,
};

const initialEnvironment = {
id: 'env123',
state: 'RUNNING',
firstTaskInError: firstTaskInError,
};

environmentCacheService.addOrUpdateEnvironment(initialEnvironment);

assert.strictEqual(environmentCacheService._environments.size, 1);
assert.deepStrictEqual(
environmentCacheService._environments.get('env123').firstTaskInError,
firstTaskInError
);

const updatedEnvironment = {
id: 'env123',
state: 'CONFIGURED',
someOtherField: 'newValue',
};

environmentCacheService.addOrUpdateEnvironment(updatedEnvironment);

assert.strictEqual(environmentCacheService._environments.size, 1);
const cachedEnv = environmentCacheService._environments.get('env123');
assert.strictEqual(cachedEnv.state, 'CONFIGURED');
assert.strictEqual(cachedEnv.someOtherField, 'newValue');
assert.deepStrictEqual(cachedEnv.firstTaskInError, firstTaskInError,
'firstTaskInError should be preserved after update');
});
});

describe('`get environments` method', () => {
Expand Down Expand Up @@ -332,12 +374,12 @@ describe(`'EnvironmentCacheService' - test suite`, () => {
environmentCacheService.addOrUpdateEnvironment(initialEnvironment);
const firstTaskInErrorEventSent = {
environmentId: 'env1',
state: 'ERROR',
state: 'ERROR_CRITICAL',
taskid: 1,
name: 'task1',
hostname: 'host1',
className: 'class1',
isCritical: false,
isCritical: true,
};
eventEmitter.emit(TASKS_TRACK, {
timestamp: Date.now(),
Expand Down