Skip to content

Commit 651d412

Browse files
author
wayne
authored
Merge branch 'main' into fix-argo-lint-offline-issue-on-multi-documents-yaml-file
2 parents 0c5b6aa + 07703ab commit 651d412

3 files changed

Lines changed: 130 additions & 2 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import {Checkbox} from 'argo-ui/src/components/checkbox';
2+
import {Tooltip} from 'argo-ui/src/components/tooltip/tooltip';
3+
import React, {useState} from 'react';
4+
5+
import {Parameter, RetryOpts, Workflow} from '../../../models';
6+
import {ParametersInput} from '../../shared/components/parameters-input';
7+
import {services} from '../../shared/services';
8+
import {Utils} from '../../shared/utils';
9+
import {ErrorNotice} from '../../shared/components/error-notice';
10+
11+
interface Props {
12+
nodeId: string;
13+
workflow: Workflow;
14+
isArchived: boolean;
15+
isWorkflowInCluster: boolean;
16+
onRetrySuccess: () => void;
17+
}
18+
19+
export function RetryWorkflowNode(props: Props) {
20+
const [overrideParameters, setOverrideParameters] = useState(false);
21+
const [restartSuccessful, setRestartSuccessful] = useState(false);
22+
const [workflowParameters, setWorkflowParameters] = useState<Parameter[]>(JSON.parse(JSON.stringify(props.workflow.spec.arguments.parameters || [])));
23+
const [error, setError] = useState<Error>();
24+
25+
async function submit() {
26+
const parameters: RetryOpts['parameters'] = overrideParameters
27+
? [...workflowParameters.filter(p => Utils.getValueFromParameter(p) !== undefined).map(p => p.name + '=' + Utils.getValueFromParameter(p))]
28+
: [];
29+
const opts: RetryOpts = {
30+
parameters,
31+
restartSuccessful,
32+
nodeFieldSelector: `id=${props.nodeId}`
33+
};
34+
35+
try {
36+
props.isArchived && !props.isWorkflowInCluster
37+
? await services.workflows.retryArchived(props.workflow.metadata.uid, props.workflow.metadata.namespace, opts)
38+
: await services.workflows.retry(props.workflow.metadata.name, props.workflow.metadata.namespace, opts);
39+
props.onRetrySuccess();
40+
} catch (err) {
41+
setError(err);
42+
}
43+
}
44+
45+
return (
46+
<div style={{padding: 16}}>
47+
<h4>Retry Node</h4>
48+
<h5>
49+
{props.workflow.metadata.namespace}/{props.nodeId}
50+
</h5>
51+
52+
<p>Note: Retrying this node will re-execute this node and all downstream nodes.</p>
53+
54+
{error && <ErrorNotice error={error} />}
55+
<div className='white-box'>
56+
{/* Override Parameters */}
57+
<div key='override-parameters' style={{marginBottom: 25}}>
58+
<label>Override Parameters</label>
59+
<div className='columns small-9'>
60+
<Checkbox checked={overrideParameters} onChange={setOverrideParameters} />
61+
</div>
62+
</div>
63+
64+
{overrideParameters && (
65+
<div key='parameters' style={{marginBottom: 25}}>
66+
<label>Parameters</label>
67+
{workflowParameters.length > 0 ? (
68+
<ParametersInput parameters={workflowParameters} onChange={setWorkflowParameters} />
69+
) : (
70+
<>
71+
<br />
72+
<label>No parameters</label>
73+
</>
74+
)}
75+
</div>
76+
)}
77+
78+
{/* Restart Successful */}
79+
<div key='restart-successful' style={{marginBottom: 25}}>
80+
<label>
81+
Restart Successful{' '}
82+
<Tooltip content='Leaving this box unchecked avoids re-running nodes that have run successfully before'>
83+
<i className='fa fa-question-circle' style={{marginLeft: 4}} />
84+
</Tooltip>
85+
</label>
86+
<div className='columns small-9'>
87+
<Checkbox checked={restartSuccessful} onChange={setRestartSuccessful} />
88+
</div>
89+
</div>
90+
91+
{/* Retry button */}
92+
<div key='retry'>
93+
<button onClick={submit} className='argo-button argo-button--base'>
94+
<i className='fa fa-undo-alt' /> Retry
95+
</button>
96+
</div>
97+
</div>
98+
</div>
99+
);
100+
}

ui/src/app/workflows/components/workflow-details/workflow-details.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {SuspendInputs} from './suspend-inputs';
4343
import {WorkflowResourcePanel} from './workflow-resource-panel';
4444

4545
import './workflow-details.scss';
46+
import {RetryWorkflowNode} from '../retry-workflow-node-panel';
4647

4748
function parseSidePanelParam(param: string) {
4849
const [type, nodeId, container] = (param || '').split(':');
@@ -102,6 +103,7 @@ export function WorkflowDetails({history, location, match}: RouteComponentProps<
102103
const [nodeId, setNodeId] = useState(queryParams.get('nodeId'));
103104
const [nodePanelView, setNodePanelView] = useState(queryParams.get('nodePanelView'));
104105
const [sidePanel, setSidePanel] = useState(queryParams.get('sidePanel'));
106+
const [showRetryNode, setShowRetryNode] = useState<boolean>();
105107
const [parameters, setParameters] = useState<Parameter[]>([]);
106108
const sidePanelRef = useRef<HTMLDivElement>(null);
107109
const [workflow, setWorkflow] = useState<Workflow>();
@@ -528,11 +530,29 @@ export function WorkflowDetails({history, location, match}: RouteComponentProps<
528530
transition: isSidePanelAnimating ? `width ${ANIMATION_MS}ms` : 'unset',
529531
width: isSidePanelExpanded ? `${sidePanelWidth}px` : 0
530532
}}>
531-
<button className='workflow-details__step-info-close' onClick={() => setNodeId(null)}>
533+
<button
534+
className='workflow-details__step-info-close'
535+
onClick={() => {
536+
if (showRetryNode) {
537+
setShowRetryNode(false);
538+
} else {
539+
setNodeId(null);
540+
}
541+
}}>
532542
<i className='argo-icon-close' />
533543
</button>
544+
534545
<div className='workflow-details__step-info-drag-handle' {...sidePanelDragHandleProps} />
535-
{selectedNode && (
546+
{selectedNode && showRetryNode && (
547+
<RetryWorkflowNode
548+
nodeId={selectedNode.id}
549+
workflow={workflow}
550+
isArchived={isArchivedWorkflow(workflow)}
551+
isWorkflowInCluster={isWorkflowInCluster(workflow)}
552+
onRetrySuccess={() => setShowRetryNode(false)}
553+
/>
554+
)}
555+
{selectedNode && !showRetryNode && (
536556
<WorkflowNodeInfo
537557
node={selectedNode}
538558
onTabSelected={setNodePanelView}
@@ -542,6 +562,7 @@ export function WorkflowDetails({history, location, match}: RouteComponentProps<
542562
onShowContainerLogs={(x, container) => setSidePanel(`logs:${x}:${container}`)}
543563
onShowEvents={() => setSidePanel(`events:${nodeId}`)}
544564
onShowYaml={() => setSidePanel(`yaml:${nodeId}`)}
565+
onRetryNode={() => setShowRetryNode(true)}
545566
archived={archived}
546567
onResume={() => renderResumePopup()}
547568
/>

ui/src/app/workflows/components/workflow-node-info/workflow-node-info.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ interface Props {
6767
onTabSelected?: (tabSelected: string) => void;
6868
selectedTabKey?: string;
6969
onResume?: () => void;
70+
onRetryNode?: () => void;
7071
}
7172

7273
const AttributeRow = (attr: {title: string; value: any}) => (
@@ -174,6 +175,7 @@ function WorkflowNodeSummary(props: Props) {
174175
});
175176
}
176177
const showLogs = (x = 'main') => props.onShowContainerLogs(props.node.id, x);
178+
177179
return (
178180
<div className='white-box'>
179181
<div className='white-box__details' style={{paddingBottom: '8px'}}>
@@ -190,6 +192,11 @@ function WorkflowNodeSummary(props: Props) {
190192
MANIFEST
191193
</Button>
192194
)}{' '}
195+
{props.onRetryNode && ['Succeeded', 'Failed'].includes(props.node.phase) && (
196+
<Button icon='undo-alt' onClick={() => props.onRetryNode()}>
197+
RETRY NODE
198+
</Button>
199+
)}{' '}
193200
{props.node.type === 'Pod' && props.onShowContainerLogs && (
194201
<Button onClick={() => showLogs()} icon='bars'>
195202
LOGS

0 commit comments

Comments
 (0)