Skip to content

Commit

Permalink
feat(ui): Add warning and confirmation popup when force/replace sync …
Browse files Browse the repository at this point in the history
…is selected (argoproj#8574)

Signed-off-by: Yuan Tang <terrytangyuan@gmail.com>
Signed-off-by: wojtekidd <wojtek.cichon@protonmail.com>
  • Loading branch information
terrytangyuan authored and wojtekidd committed Apr 25, 2022
1 parent 2514dbc commit b9d4b67
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as ReactForm from 'react-form';
require('./application-sync-options.scss');

export const REPLACE_WARNING = `The resources will be synced using 'kubectl replace/create' command that is a potentially destructive action and might cause resources recreation.`;
export const FORCE_WARNING = `The resources will be synced using '--force' that is a potentially destructive action and will immediately remove resources from the API and bypasses graceful deletion. Immediate deletion of some resources may result in inconsistency or data loss.`;

export interface ApplicationSyncOptionProps {
options: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Consumer} from '../../../shared/context';
import * as models from '../../../shared/models';
import {services} from '../../../shared/services';
import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options';
import {ApplicationManualSyncFlags, ApplicationSyncOptions, SyncFlags, REPLACE_WARNING} from '../application-sync-options/application-sync-options';
import {ApplicationManualSyncFlags, ApplicationSyncOptions, FORCE_WARNING, SyncFlags, REPLACE_WARNING} from '../application-sync-options/application-sync-options';
import {ComparisonStatusIcon, nodeKey} from '../utils';

require('./application-sync-panel.scss');
Expand Down Expand Up @@ -60,7 +60,6 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app
if (resources.length === appResources.length) {
resources = null;
}

const replace = params.syncOptions?.findIndex((opt: string) => opt === 'Replace=true') > -1;
if (replace) {
const confirmed = await ctx.popup.confirm('Synchronize using replace?', () => (
Expand All @@ -75,11 +74,23 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app
}

const syncFlags = {...params.syncFlags} as SyncFlags;
const force = syncFlags.Force || false;

if (syncFlags.ApplyOnly) {
syncStrategy.apply = {force: syncFlags.Force || false};
syncStrategy.apply = {force};
} else {
syncStrategy.hook = {force: syncFlags.Force || false};
syncStrategy.hook = {force};
}
if (force) {
const confirmed = await ctx.popup.confirm('Synchronize with force?', () => (
<div>
<i className='fa fa-exclamation-triangle' style={{color: ARGO_WARNING_COLOR}} /> {FORCE_WARNING} Are you sure you want to continue?
</div>
));
if (!confirmed) {
setPending(false);
return;
}
}

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {ErrorNotification, FormField, NotificationType, SlidingPanel} from 'argo-ui';
import * as React from 'react';
import {Form, FormApi} from 'react-form';
import {ProgressPopup} from '../../../shared/components';
import {ARGO_WARNING_COLOR, ProgressPopup, Spinner} from '../../../shared/components';
import {Consumer} from '../../../shared/context';
import * as models from '../../../shared/models';
import {services} from '../../../shared/services';
import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options';
import {ApplicationManualSyncFlags, ApplicationSyncOptions, SyncFlags} from '../application-sync-options/application-sync-options';
import {ApplicationManualSyncFlags, ApplicationSyncOptions, FORCE_WARNING, REPLACE_WARNING, SyncFlags} from '../application-sync-options/application-sync-options';
import {ApplicationSelector} from '../../../shared/components';

interface Progress {
Expand All @@ -18,6 +18,7 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
const [form, setForm] = React.useState<FormApi>(null);
const [progress, setProgress] = React.useState<Progress>(null);
const getSelectedApps = (params: any) => apps.filter((_, i) => params['app/' + i]);
const [isPending, setPending] = React.useState(false);
return (
<Consumer>
{ctx => (
Expand All @@ -27,7 +28,8 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
onClose={() => hide()}
header={
<div>
<button className='argo-button argo-button--base' onClick={() => form.submitForm(null)}>
<button className='argo-button argo-button--base' disabled={isPending} onClick={() => form.submitForm(null)}>
<Spinner show={isPending} style={{marginRight: '5px'}} />
Sync
</button>{' '}
<button onClick={() => hide()} className='argo-button argo-button--base-o'>
Expand All @@ -38,16 +40,40 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
<Form
defaultValues={{syncFlags: []}}
onSubmit={async (params: any) => {
setPending(true);
const selectedApps = getSelectedApps(params);
const syncFlags = {...params.syncFlags} as SyncFlags;
const force = syncFlags.Force || false;
if (force) {
const confirmed = await ctx.popup.confirm('Synchronize with force?', () => (
<div>
<i className='fa fa-exclamation-triangle' style={{color: ARGO_WARNING_COLOR}} /> {FORCE_WARNING} Are you sure you want to continue?
</div>
));
if (!confirmed) {
setPending(false);
return;
}
}
const replace = params.syncOptions?.findIndex((opt: string) => opt === 'Replace=true') > -1;
if (replace) {
const confirmed = await ctx.popup.confirm('Synchronize using replace?', () => (
<div>
<i className='fa fa-exclamation-triangle' style={{color: ARGO_WARNING_COLOR}} /> {REPLACE_WARNING} Are you sure you want to continue?
</div>
));
if (!confirmed) {
setPending(false);
return;
}
}
if (selectedApps.length === 0) {
ctx.notifications.show({content: `No apps selected`, type: NotificationType.Error});
setPending(false);
return;
}

const syncFlags = {...params.syncFlags} as SyncFlags;

const syncStrategy: models.SyncStrategy =
syncFlags.ApplyOnly || false ? {apply: {force: syncFlags.Force || false}} : {hook: {force: syncFlags.Force || false}};
const syncStrategy: models.SyncStrategy = syncFlags.ApplyOnly || false ? {apply: {force}} : {hook: {force}};

setProgress({percentage: 0, title: 'Starting...'});
let i = 0;
Expand All @@ -68,6 +94,9 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
content: <ErrorNotification title={`Unable to sync ${app.metadata.name}`} e={e} />,
type: NotificationType.Error
});
})
.finally(() => {
setPending(false);
});
i++;
setProgress({
Expand Down

0 comments on commit b9d4b67

Please sign in to comment.