Skip to content

Commit

Permalink
feat: Add restart apps button on Applications List view
Browse files Browse the repository at this point in the history
  • Loading branch information
audip committed Dec 23, 2024
1 parent f245e8b commit fc1249f
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {ApplicationsSummary} from './applications-summary';
import {ApplicationsTable} from './applications-table';
import {ApplicationTiles} from './applications-tiles';
import {ApplicationsRefreshPanel} from '../applications-refresh-panel/applications-refresh-panel';
import {ApplicationsRestartPanel} from '../applications-restart-panel/applications-restart-panel';
import {useSidebarTarget} from '../../../sidebar/sidebar';

import './applications-list.scss';
Expand Down Expand Up @@ -314,6 +315,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => {
const appInput = tryJsonParse(query.get('new'));
const syncAppsInput = tryJsonParse(query.get('syncApps'));
const refreshAppsInput = tryJsonParse(query.get('refreshApps'));
const restartAppsInput = tryJsonParse(query.get('restartApps'));
const [createApi, setCreateApi] = React.useState(null);
const clusters = React.useMemo(() => services.clusters.list(), []);
const [isAppCreatePending, setAppCreatePending] = React.useState(false);
Expand Down Expand Up @@ -477,6 +479,11 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => {
title: 'Refresh Apps',
iconClassName: 'fa fa-redo',
action: () => ctx.navigation.goto('.', {refreshApps: true}, {replace: true})
},
{
title: 'Restart Apps',
iconClassName: 'fa fa-recycle',
action: () => ctx.navigation.goto('.', {restartApps: true}, {replace: true})
}
]
}
Expand Down Expand Up @@ -586,6 +593,12 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => {
hide={() => ctx.navigation.goto('.', {refreshApps: null}, {replace: true})}
apps={filteredApps}
/>
<ApplicationsRestartPanel
key='restartPanel'
show={restartAppsInput}
hide={() => ctx.navigation.goto('.', {restartApps: null}, {replace: true})}
apps={filteredApps}
/>
</div>
<ObservableQuery>
{q => (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import * as React from 'react';
import {SlidingPanel, Spinner, ErrorNotification, NotificationType} from 'argo-ui';
import {Form, FormApi} from 'react-form';
import {ProgressPopup} from '../../../shared/components';
import {Consumer} from '../../../shared/context';
import * as models from '../../../shared/models';
import {services} from '../../../shared/services';
import {ApplicationSelector} from '../../../shared/components';

interface Progress {
percentage: number;
title: string;
}

export const ApplicationsRestartPanel = ({show, apps, hide}: {show: boolean; apps: models.Application[]; hide: () => void}) => {
const [form, setForm] = React.useState<FormApi>(null);
const [progress, setProgress] = React.useState<Progress>(null);
const [isRestartPending, setRestartPending] = React.useState(false);

const getSelectedApps = (params: any) => apps.filter((_, i) => params['app/' + i]);

return (
<Consumer>
{ctx => (
<SlidingPanel
isMiddle={true}
isShown={show}
onClose={hide}
header={
<div>
<button className='argo-button argo-button--base' onClick={() => form.submitForm(null)}>
Restart
</button>{' '}
<button onClick={hide} className='argo-button argo-button--base-o'>
Cancel
</button>
</div>
}>
<Form
onSubmit={async (params: any) => {
const selectedApps = getSelectedApps(params);
if (selectedApps.length === 0) {
ctx.notifications.show({content: `No apps selected`, type: NotificationType.Error});
return;
}

setProgress({percentage: 0, title: 'Restarting applications'});
setRestartPending(true);
let i = 0;
const restartActions = [];
for (const app of selectedApps) {
const restartAction = async () => {
try {
const tree = await services.applications.resourceTree(app.metadata.name, app.metadata.namespace);
const relevantResources = (tree?.nodes || []).filter(
n => (n.kind === 'Deployment' || n.kind === 'StatefulSet' || n.kind === 'DaemonSet') && n.group === 'apps'
);
for (const resource of relevantResources) {
await services.applications.runResourceAction(app.metadata.name, app.metadata.namespace, resource, 'restart');
}
} catch (e) {
ctx.notifications.show({
content: <ErrorNotification title={`Unable to restart resources in ${app.metadata.name}`} e={e} />,
type: NotificationType.Error
});
}
i++;
setProgress({
percentage: i / selectedApps.length,
title: `Restarted ${i} of ${selectedApps.length} applications`
});
};
restartActions.push(restartAction());

if (restartActions.length >= 20) {
await Promise.all(restartActions);
restartActions.length = 0;
}
}
await Promise.all(restartActions);
setRestartPending(false);
hide();
}}
getApi={setForm}>
{formApi => (
<React.Fragment>
<div className='argo-form-row' style={{marginTop: 0}}>
<h4>Restart app(s)</h4>
{progress !== null && <ProgressPopup onClose={() => setProgress(null)} percentage={progress.percentage} title={progress.title} />}
<ApplicationSelector apps={apps} formApi={formApi} />
</div>
</React.Fragment>
)}
</Form>
</SlidingPanel>
)}
</Consumer>
);
};

0 comments on commit fc1249f

Please sign in to comment.