Skip to content

Commit 4aa0a8c

Browse files
Merge pull request #2833 from jenny-s51/iss4999
[RHOAIENG-4999] Details page for archived runs
2 parents c9caed3 + ede387d commit 4aa0a8c

File tree

7 files changed

+115
-20
lines changed

7 files changed

+115
-20
lines changed

frontend/src/api/pipelines/custom.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ export const stopPipelineRun: UpdatePipelineRunAPI = (hostPath) => (opts, runId)
235235
proxyENDPOINT(hostPath, `/apis/v2beta1/runs/${runId}:terminate`, {}, opts),
236236
);
237237

238+
export const retryPipelineRun: UpdatePipelineRunAPI = (hostPath) => (opts, runId) =>
239+
handlePipelineFailures(proxyENDPOINT(hostPath, `/apis/v2beta1/runs/${runId}:retry`, {}, opts));
240+
238241
export const updatePipelineRunJob: UpdatePipelineRunJobAPI = (hostPath) => (opts, jobId, enabled) =>
239242
handlePipelineFailures(
240243
proxyENDPOINT(

frontend/src/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { Label, Split, SplitItem } from '@patternfly/react-core';
3-
import { PipelineRunKFv2 } from '~/concepts/pipelines/kfTypes';
3+
import { PipelineRunKFv2, StorageStateKF } from '~/concepts/pipelines/kfTypes';
44
import { computeRunStatus } from '~/concepts/pipelines/content/utils';
55
import PipelineRunTypeLabel from '~/concepts/pipelines/content/PipelineRunTypeLabel';
66

@@ -17,6 +17,8 @@ const PipelineDetailsTitle: React.FC<RunJobTitleProps> = ({
1717
}) => {
1818
const { icon, label } = computeRunStatus(run);
1919

20+
const isArchived = run.storage_state === StorageStateKF.ARCHIVED;
21+
2022
return (
2123
<>
2224
<Split hasGutter>
@@ -31,6 +33,11 @@ const PipelineDetailsTitle: React.FC<RunJobTitleProps> = ({
3133
<Label icon={icon}>{label}</Label>
3234
</SplitItem>
3335
)}
36+
{isArchived && (
37+
<SplitItem>
38+
<Label>Archived</Label>
39+
</SplitItem>
40+
)}
3441
</Split>
3542
</>
3643
);

frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetails.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
RunDetailsTabs,
2828
RunDetailsTabSelection,
2929
} from '~/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDrawerBottomTabs';
30+
import { ArchiveRunModal } from '~/pages/pipelines/global/runs/ArchiveRunModal';
3031
import DeletePipelineRunsModal from '~/concepts/pipelines/content/DeletePipelineRunsModal';
3132
import { usePipelinesAPI } from '~/concepts/pipelines/context';
3233
import PipelineDetailsTitle from '~/concepts/pipelines/content/pipelinesDetails/PipelineDetailsTitle';
@@ -53,6 +54,8 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
5354
);
5455
const pipelineSpec = version?.pipeline_spec ?? run?.pipeline_spec;
5556
const [deleting, setDeleting] = React.useState(false);
57+
const [archiving, setArchiving] = React.useState(false);
58+
5659
const [detailsTab, setDetailsTab] = React.useState<RunDetailsTabSelection>(
5760
RunDetailsTabs.DETAILS,
5861
);
@@ -164,7 +167,11 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
164167
</Breadcrumb>
165168
}
166169
headerAction={
167-
<PipelineRunDetailsActions run={run} onDelete={() => setDeleting(true)} />
170+
<PipelineRunDetailsActions
171+
run={run}
172+
onDelete={() => setDeleting(true)}
173+
onArchive={() => setArchiving(true)}
174+
/>
168175
}
169176
empty={false}
170177
>
@@ -202,6 +209,11 @@ const PipelineRunDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath,
202209
}
203210
}}
204211
/>
212+
<ArchiveRunModal
213+
isOpen={archiving}
214+
runs={run ? [run] : []}
215+
onCancel={() => setArchiving(false)}
216+
/>
205217
</>
206218
);
207219
};

frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/PipelineRunDetailsActions.tsx

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,54 @@
11
import * as React from 'react';
2+
import { Tooltip } from '@patternfly/react-core';
23
import {
34
Dropdown,
45
DropdownItem,
56
DropdownSeparator,
67
DropdownToggle,
78
} from '@patternfly/react-core/deprecated';
8-
import { useNavigate, useParams } from 'react-router-dom';
9+
import { useNavigate } from 'react-router-dom';
910
import { usePipelinesAPI } from '~/concepts/pipelines/context';
1011
import useNotification from '~/utilities/useNotification';
1112
import { PipelineRunKFv2, RuntimeStateKF, StorageStateKF } from '~/concepts/pipelines/kfTypes';
1213
import { cloneRunRoute, experimentsCompareRunsRoute } from '~/routes';
1314
import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas';
15+
import useExperimentById from '~/concepts/pipelines/apiHooks/useExperimentById';
1416

1517
type PipelineRunDetailsActionsProps = {
1618
run?: PipelineRunKFv2 | null;
19+
onArchive: () => void;
1720
onDelete: () => void;
1821
};
1922

20-
const PipelineRunDetailsActions: React.FC<PipelineRunDetailsActionsProps> = ({ onDelete, run }) => {
23+
const PipelineRunDetailsActions: React.FC<PipelineRunDetailsActionsProps> = ({
24+
onDelete,
25+
onArchive,
26+
run,
27+
}) => {
2128
const navigate = useNavigate();
22-
const { experimentId } = useParams();
23-
const { namespace, api } = usePipelinesAPI();
29+
const { namespace, api, refreshAllAPI } = usePipelinesAPI();
2430
const notification = useNotification();
2531
const [open, setOpen] = React.useState(false);
2632
const isExperimentsAvailable = useIsAreaAvailable(SupportedArea.PIPELINE_EXPERIMENTS).status;
2733
const isRunActive = run?.storage_state === StorageStateKF.AVAILABLE;
34+
const [experiment] = useExperimentById(run?.experiment_id);
35+
const isExperimentActive = experiment?.storage_state === StorageStateKF.AVAILABLE;
36+
37+
const RestoreDropdownItem = (
38+
<DropdownItem
39+
isDisabled={!isExperimentActive}
40+
key="restore-run"
41+
onClick={() =>
42+
run &&
43+
api
44+
.unarchivePipelineRun({}, run.run_id)
45+
.catch((e) => notification.error('Unable to restore pipeline run', e.message))
46+
.then(() => refreshAllAPI())
47+
}
48+
>
49+
Restore
50+
</DropdownItem>
51+
);
2852

2953
return (
3054
<Dropdown
@@ -42,15 +66,15 @@ const PipelineRunDetailsActions: React.FC<PipelineRunDetailsActionsProps> = ({ o
4266
? []
4367
: [
4468
<DropdownItem
45-
key="stop-run"
46-
isDisabled={run.state !== RuntimeStateKF.RUNNING}
69+
key="retry-run"
70+
isDisabled={run.state !== RuntimeStateKF.FAILED || !!run.error}
4771
onClick={() =>
4872
api
49-
.stopPipelineRun({}, run.run_id)
50-
.catch((e) => notification.error('Unable to stop pipeline run', e.message))
73+
.retryPipelineRun({}, run.run_id)
74+
.catch((e) => notification.error('Unable to retry pipeline run', e.message))
5175
}
5276
>
53-
Stop
77+
Retry
5478
</DropdownItem>,
5579
<DropdownItem
5680
key="clone-run"
@@ -59,29 +83,68 @@ const PipelineRunDetailsActions: React.FC<PipelineRunDetailsActionsProps> = ({ o
5983
cloneRunRoute(
6084
namespace,
6185
run.run_id,
62-
isExperimentsAvailable ? experimentId : undefined,
86+
isExperimentsAvailable ? run.experiment_id : undefined,
6387
),
6488
)
6589
}
6690
>
6791
Duplicate
6892
</DropdownItem>,
69-
isExperimentsAvailable && experimentId && isRunActive ? (
93+
<DropdownItem
94+
key="stop-run"
95+
isDisabled={run.state !== RuntimeStateKF.RUNNING}
96+
onClick={() =>
97+
api
98+
.stopPipelineRun({}, run.run_id)
99+
.catch((e) => notification.error('Unable to stop pipeline run', e.message))
100+
}
101+
>
102+
Stop
103+
</DropdownItem>,
104+
isExperimentsAvailable && run.experiment_id && isRunActive ? (
70105
<DropdownItem
71106
key="compare-runs"
72107
onClick={() =>
73-
navigate(experimentsCompareRunsRoute(namespace, experimentId, [run.run_id]))
108+
navigate(
109+
experimentsCompareRunsRoute(namespace, run.experiment_id, [run.run_id]),
110+
)
74111
}
75112
>
76113
Compare runs
77114
</DropdownItem>
78115
) : (
79116
<React.Fragment key="compare-runs" />
80117
),
81-
<DropdownSeparator key="separator" />,
82-
<DropdownItem key="delete-run" onClick={() => onDelete()}>
83-
Delete
84-
</DropdownItem>,
118+
!isRunActive ? (
119+
!isExperimentActive ? (
120+
<Tooltip
121+
position="left"
122+
content={
123+
<div>
124+
Archived runs cannot be restored until its associated experiment is
125+
restored.
126+
</div>
127+
}
128+
>
129+
{RestoreDropdownItem}
130+
</Tooltip>
131+
) : (
132+
RestoreDropdownItem
133+
)
134+
) : (
135+
<React.Fragment key="restore-run" />
136+
),
137+
!isRunActive ? (
138+
<React.Fragment key="delete-run">
139+
<DropdownSeparator key="separator" />
140+
<DropdownItem onClick={() => onDelete()}>Delete</DropdownItem>
141+
</React.Fragment>
142+
) : (
143+
<React.Fragment key="archive-run">
144+
<DropdownSeparator key="separator" />
145+
<DropdownItem onClick={() => onArchive()}>Archive</DropdownItem>
146+
</React.Fragment>
147+
),
85148
]
86149
}
87150
/>

frontend/src/concepts/pipelines/content/tables/experiment/ExperimentTableRow.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
33

44
import { ActionsColumn, IAction, Td, Tr } from '@patternfly/react-table';
55

6-
import { ExperimentKFv2 } from '~/concepts/pipelines/kfTypes';
6+
import { ExperimentKFv2, StorageStateKF } from '~/concepts/pipelines/kfTypes';
77
import { CheckboxTd } from '~/components/table';
88
import { experimentRunsRoute } from '~/routes';
99
import { usePipelinesAPI } from '~/concepts/pipelines/context';
@@ -24,11 +24,18 @@ const ExperimentTableRow: React.FC<ExperimentTableRowProps> = ({
2424
}) => {
2525
const { namespace } = usePipelinesAPI();
2626

27+
const isArchived = experiment.storage_state === StorageStateKF.ARCHIVED;
28+
2729
return (
2830
<Tr>
2931
<CheckboxTd id={experiment.experiment_id} isChecked={isChecked} onToggle={onToggleCheck} />
3032
<Td dataLabel="Experiment">
31-
<Link to={experimentRunsRoute(namespace, experiment.experiment_id)} state={{ experiment }}>
33+
<Link
34+
to={`${experimentRunsRoute(namespace, experiment.experiment_id)}${
35+
isArchived ? '/?runType=archived' : ''
36+
}`}
37+
state={{ experiment }}
38+
>
3239
{experiment.display_name}
3340
</Link>
3441
</Td>

frontend/src/concepts/pipelines/context/usePipelineAPIState.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
archiveExperiment,
3131
unarchiveExperiment,
3232
deleteExperiment,
33+
retryPipelineRun,
3334
} from '~/api';
3435
import { PipelineAPIs } from '~/concepts/pipelines/types';
3536
import { APIState } from '~/concepts/proxy/types';
@@ -66,6 +67,7 @@ const usePipelineAPIState = (
6667
listPipelineVersions: listPipelineVersions(path),
6768
archivePipelineRun: archivePipelineRun(path),
6869
unarchivePipelineRun: unarchivePipelineRun(path),
70+
retryPipelineRun: retryPipelineRun(path),
6971
archiveExperiment: archiveExperiment(path),
7072
unarchiveExperiment: unarchiveExperiment(path),
7173
stopPipelineRun: stopPipelineRun(path),

frontend/src/concepts/pipelines/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export type PipelineAPIs = {
152152
listPipelineRunJobs: ListPipelineRunJobs;
153153
listPipelineVersions: ListPipelineVersions;
154154
archivePipelineRun: UpdatePipelineRun;
155+
retryPipelineRun: UpdatePipelineRun;
155156
unarchivePipelineRun: UpdatePipelineRun;
156157
archiveExperiment: UpdateExperiment;
157158
unarchiveExperiment: UpdateExperiment;

0 commit comments

Comments
 (0)