-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create service to restart wazuh (#4365)
* file to be added with the restart functions * restart calls were changed to service calls * the restarting on time sign appears * modal while restarting added * countDown change of location * cleaning * modal redesign * model and code update * modified delays and designs * change text Synchronizing cluster * Update restart-cluster-manager-callout.tsx * Code improvements * suggestions implemented * polling sync add * Code improvements * add endpoint * add comment * add close button * suggestions implemented * add modal wazuh restarted * delete unnecessary toast * Set clearState on restart error * disabled unneeded console.log Co-authored-by: Alex Ruiz Becerra <[email protected]> Co-authored-by: Federico Rodriguez <[email protected]> Co-authored-by: chantal.kelm <[email protected]> (cherry picked from commit 6598df9)
- Loading branch information
1 parent
9278ed2
commit 24331d6
Showing
22 changed files
with
945 additions
and
607 deletions.
There are no files selected for viewing
130 changes: 0 additions & 130 deletions
130
public/components/common/restart-cluster-manager-callout.tsx
This file was deleted.
Oops, something went wrong.
258 changes: 258 additions & 0 deletions
258
public/components/common/restart-modal/restart-modal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { | ||
EuiButton, | ||
EuiButtonEmpty, | ||
EuiEmptyPrompt, | ||
EuiEmptyPromptProps, | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiOverlayMask, | ||
EuiProgress, | ||
} from '@elastic/eui'; | ||
import { getHttp } from '../../../kibana-services'; | ||
import { WazuhConfig } from '../../../react-services/wazuh-config'; | ||
import { RestartHandler } from '../../../react-services/wz-restart'; | ||
import { getAssetURL, getThemeAssetURL } from '../../../utils/assets'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { | ||
updateRestartStatus, | ||
updateRestartAttempt, | ||
updateSyncCheckAttempt, | ||
updateUnsynchronizedNodes, | ||
} from '../../../redux/actions/appStateActions'; | ||
import { UI_LOGGER_LEVELS } from '../../../../common/constants'; | ||
import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/types'; | ||
import { getErrorOrchestrator } from '../../../react-services/common-services'; | ||
|
||
/** | ||
* The restart modal to show feedback to the user. | ||
* @param props component's props | ||
* @returns components's body | ||
*/ | ||
export const RestartModal = (props: { useDelay?: boolean, showWarningRestart? }) => { | ||
// Component props | ||
const { useDelay = false } = props; | ||
|
||
// Use default HEALTHCHECK_DELAY | ||
const [timeToHC, setTimeToHC] = useState(RestartHandler.HEALTHCHECK_DELAY / 1000); | ||
|
||
// Use default SYNC_DELAY | ||
const [syncDelay, setSyncDelay] = useState(RestartHandler.SYNC_DELAY / 1000); | ||
|
||
// Restart polling counter | ||
const restartAttempt = useSelector((state) => state.appStateReducers.restartAttempt); | ||
|
||
// Cluster nodes that did not synced | ||
const unsyncedNodes = useSelector((state) => state.appStateReducers.unsynchronizedNodes); | ||
|
||
// Sync polling counter | ||
const syncPollingAttempt = useSelector((state) => state.appStateReducers.syncCheckAttempt); | ||
|
||
// Current status of the restarting process | ||
const restartStatus = useSelector((state) => state.appStateReducers.restartStatus); | ||
|
||
// Max attempts = MAX_SYNC_POLLING_ATTEMPTS + MAX_RESTART_POLLING_ATTEMPTS | ||
const maxAttempts = useDelay | ||
? RestartHandler.MAX_RESTART_POLLING_ATTEMPTS + RestartHandler.MAX_SYNC_POLLING_ATTEMPTS | ||
: RestartHandler.MAX_RESTART_POLLING_ATTEMPTS; | ||
|
||
// Current attempt | ||
const attempts = useDelay ? restartAttempt + syncPollingAttempt : restartAttempt; | ||
|
||
// Load Wazuh logo | ||
const wzConfig = new WazuhConfig().getConfig(); | ||
const logotypeURL = getHttp().basePath.prepend( | ||
wzConfig['customization.logo.sidebar'] | ||
? getAssetURL(wzConfig['customization.logo.sidebar']) | ||
: getThemeAssetURL('icon.svg') | ||
); | ||
|
||
// Apply SYNC_DELAY if useDelay prop is enabled. | ||
useEffect(() => { | ||
if (useDelay) { | ||
countdown(syncDelay, setSyncDelay); | ||
} | ||
}, []); | ||
|
||
// Apply HEALTHCHECK_DELAY when the restart has failed | ||
useEffect(() => { | ||
restartStatus === RestartHandler.RESTART_STATES.RESTART_ERROR && | ||
countdown(timeToHC, setTimeToHC); | ||
|
||
restartStatus === RestartHandler.RESTART_STATES.RESTARTED_INFO && | ||
restartedTimeout(); | ||
}, [restartStatus]); | ||
|
||
// TODO | ||
const countdown = (time: number, setState) => { | ||
let countDown = time; | ||
|
||
const interval = setInterval(() => { | ||
setState(countDown); | ||
if (countDown === 0 && setState === setTimeToHC) { | ||
clearInterval(interval); | ||
RestartHandler.goToHealthcheck(); | ||
} else if (countDown === 0) { | ||
clearInterval(interval); | ||
} | ||
|
||
countDown--; | ||
}, 1000 /* 1 second */); | ||
}; | ||
|
||
const restartedTimeout = () => { | ||
setTimeout(() => { | ||
dispatch(updateRestartStatus(RestartHandler.RESTART_STATES.RESTARTED)); | ||
if(props.showWarningRestart){ | ||
props.showWarningRestart() | ||
} | ||
}, RestartHandler.INFO_RESTART_SUCCESS_DELAY); | ||
} | ||
|
||
// TODO review if importing these functions in wz-restart work. | ||
const dispatch = useDispatch(); | ||
const updateRedux = { | ||
updateRestartAttempt: (restartAttempt) => dispatch(updateRestartAttempt(restartAttempt)), | ||
updateSyncCheckAttempt: (syncCheckAttempt) => | ||
dispatch(updateSyncCheckAttempt(syncCheckAttempt)), | ||
updateUnsynchronizedNodes: (unsynchronizedNodes) => | ||
dispatch(updateUnsynchronizedNodes(unsynchronizedNodes)), | ||
updateRestartStatus: (restartStatus) => dispatch(updateRestartStatus(restartStatus)), | ||
}; | ||
|
||
const forceRestart = async () => { | ||
try{ | ||
await RestartHandler.restartWazuh(updateRedux); | ||
}catch(error){ | ||
const options = { | ||
context: `${RestartModal.name}.forceRestart`, | ||
level: UI_LOGGER_LEVELS.ERROR, | ||
severity: UI_ERROR_SEVERITIES.BUSINESS, | ||
error: { | ||
error: error, | ||
message: error.message || error, | ||
title: error.name || error, | ||
}, | ||
}; | ||
getErrorOrchestrator().handleError(options); | ||
} | ||
}; | ||
|
||
|
||
const abort = () => { | ||
dispatch(updateRestartStatus(RestartHandler.RESTART_STATES.RESTARTED)); | ||
dispatch(updateUnsynchronizedNodes([])); | ||
dispatch(updateRestartAttempt(0)); | ||
dispatch(updateSyncCheckAttempt(0)); | ||
} | ||
|
||
// Build the modal depending on the restart state. | ||
let emptyPromptProps: Partial<EuiEmptyPromptProps>; | ||
switch (restartStatus) { | ||
default: | ||
case RestartHandler.RESTART_STATES.RESTARTED_INFO: | ||
emptyPromptProps = { | ||
title: ( | ||
<> | ||
<img src={logotypeURL} className="wz-modal-restart-logo" alt=""></img> | ||
<h2 className="wz-modal-restart-title">Wazuh restarted</h2> | ||
</> | ||
), | ||
body: ( | ||
<> | ||
<EuiProgress value={maxAttempts} max={maxAttempts} size="s" /> | ||
</> | ||
), | ||
}; | ||
break; | ||
|
||
case RestartHandler.RESTART_STATES.RESTARTING: | ||
emptyPromptProps = { | ||
title: ( | ||
<> | ||
<img src={logotypeURL} className="wz-modal-restart-logo" alt=""></img> | ||
<h2 className="wz-modal-restart-title">Restarting Wazuh</h2> | ||
</> | ||
), | ||
body: ( | ||
<> | ||
<EuiProgress value={attempts} max={maxAttempts} size="s" /> | ||
</> | ||
), | ||
}; | ||
break; | ||
|
||
case RestartHandler.RESTART_STATES.RESTART_ERROR: | ||
emptyPromptProps = { | ||
iconType: 'alert', | ||
iconColor: 'danger', | ||
title: <h2 className="wz-modal-restart-title">Unable to connect to Wazuh.</h2>, | ||
body: <p>There was an error restarting Wazuh. The Healthcheck will run automatically.</p>, | ||
actions: ( | ||
<EuiButton color="primary" fill href="#/health-check"> | ||
Go to Healthcheck ({timeToHC} s) | ||
</EuiButton> | ||
), | ||
}; | ||
break; | ||
|
||
case RestartHandler.RESTART_STATES.SYNC_ERROR: | ||
emptyPromptProps = { | ||
iconType: 'alert', | ||
iconColor: 'danger', | ||
title: <h2 className="wz-modal-restart-title">Synchronization failed</h2>, | ||
body: ( | ||
<p> | ||
The nodes {unsyncedNodes.join(', ')} did not synchronize. Restarting Wazuh might set the | ||
cluster into an inconsistent state. Close and try again later. | ||
</p> | ||
), | ||
actions: ( | ||
<EuiFlexGroup justifyContent="flexEnd"> | ||
<EuiFlexItem grow={false}> | ||
<EuiButtonEmpty color='danger' flush="both" onClick={forceRestart}> | ||
Force restart | ||
</EuiButtonEmpty> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton color="primary" fill onClick={abort}> | ||
Close | ||
</EuiButton> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
), | ||
}; | ||
break; | ||
|
||
case RestartHandler.RESTART_STATES.SYNCING: | ||
emptyPromptProps = { | ||
title: ( | ||
<> | ||
<img src={logotypeURL} className="wz-modal-restart-logo" alt=""></img> | ||
<h2 className="wz-modal-restart-title">Synchronizing Wazuh</h2> | ||
</> | ||
), | ||
body: ( | ||
<> | ||
<EuiProgress value={attempts} max={maxAttempts} size="s" /> | ||
</> | ||
), | ||
}; | ||
break; | ||
} | ||
|
||
// | ||
return ( | ||
<EuiOverlayMask> | ||
<div | ||
className={ | ||
restartStatus === RestartHandler.RESTART_STATES.ERROR | ||
? 'wz-modal-restart-error' | ||
: 'wz-modal-restart' | ||
} | ||
> | ||
<EuiEmptyPrompt {...emptyPromptProps} /> | ||
</div> | ||
</EuiOverlayMask> | ||
); | ||
}; |
Oops, something went wrong.