1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { ContainerRequestResource, ContainerRequestState } from '../../models/container-request';
6 import { ContainerResource, ContainerState } from '../../models/container';
7 import { ResourcesState, getResource } from 'store/resources/resources';
8 import { filterResources } from '../resources/resources';
9 import { ResourceKind, Resource, extractUuidKind } from 'models/resource';
10 import { getTimeDiff } from 'common/formatters';
11 import { ArvadosTheme } from 'common/custom-theme';
13 export interface Process {
14 containerRequest: ContainerRequestResource;
15 container?: ContainerResource;
18 export enum ProcessStatus {
19 CANCELLED = 'Cancelled',
20 COMPLETED = 'Completed',
30 CANCELLING = 'Cancelling',
31 RESUBMITTED = 'Resubmitted',
34 export enum ProcessProperties {
35 FAILED_CONTAINER_RESUBMITTED = "arv:failed_container_resubmitted",
39 * Gets a process from the store using container request uuid
40 * @param uuid container request associated with process
41 * @returns a Process object with containerRequest and optional container or undefined
43 export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => {
44 if (extractUuidKind(uuid) === ResourceKind.CONTAINER_REQUEST) {
45 const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
46 if (containerRequest) {
47 if (containerRequest.containerUuid) {
48 const container = getResource<ContainerResource>(containerRequest.containerUuid)(resources);
50 return { containerRequest, container };
53 return { containerRequest };
59 export const getSubprocesses = (uuid: string) => (resources: ResourcesState) => {
60 const process = getProcess(uuid)(resources);
61 if (process && process.container) {
62 const containerRequests = filterResources(isSubprocess(process.container.uuid))(resources) as ContainerRequestResource[];
63 return containerRequests.reduce((subprocesses, { uuid }) => {
64 const process = getProcess(uuid)(resources);
66 ? [...subprocesses, process]
73 export const getProcessRuntime = ({ container }: Process) => {
75 if (container.startedAt === null) {
78 if (container.finishedAt === null) {
80 return new Date().getTime() - new Date(container.startedAt).getTime();
82 return getTimeDiff(container.finishedAt, container.startedAt);
89 export const getProcessStatusStyles = (status: string, theme: ArvadosTheme): React.CSSProperties => {
90 let color = theme.customs.colors.grey500;
93 case ProcessStatus.RUNNING:
94 color = theme.customs.colors.green800;
97 case ProcessStatus.COMPLETED:
98 case ProcessStatus.REUSED:
99 color = theme.customs.colors.green800;
101 case ProcessStatus.WARNING:
102 color = theme.customs.colors.green800;
105 case ProcessStatus.RESUBMITTED:
106 color = theme.customs.colors.darkOrange;
108 case ProcessStatus.FAILING:
109 color = theme.customs.colors.red900;
112 case ProcessStatus.CANCELLING:
113 color = theme.customs.colors.red900;
116 case ProcessStatus.CANCELLED:
117 case ProcessStatus.FAILED:
118 color = theme.customs.colors.red900;
120 case ProcessStatus.QUEUED:
121 color = theme.customs.colors.grey600;
125 color = theme.customs.colors.grey600;
129 // Using color and running we build the text, border, and background style properties
131 // Set background color when not running, otherwise use white
132 backgroundColor: running ? theme.palette.common.white : color,
133 // Set text color to status color when running, else use white text for solid button
134 color: running ? color : theme.palette.common.white,
135 // Set border color when running, else omit the style entirely
136 ...(running ? { border: `2px solid ${color}` } : {}),
140 export const getProcessStatus = ({ containerRequest, container }: Process): ProcessStatus => {
142 case containerRequest.containerUuid && !container:
143 return ProcessStatus.UNKNOWN;
145 case containerRequest.state === ContainerRequestState.UNCOMMITTED:
146 return ProcessStatus.DRAFT;
148 case containerRequest.state === ContainerRequestState.FINAL &&
149 containerRequest.properties &&
150 Boolean(containerRequest.properties[ProcessProperties.FAILED_CONTAINER_RESUBMITTED]):
151 // Failed but a new container request for the same work was resubmitted.
152 return ProcessStatus.RESUBMITTED;
154 case containerRequest.state === ContainerRequestState.FINAL &&
155 container?.state === ContainerState.RUNNING:
156 // It is about to be completed but we haven't
157 // gotten the updated container record yet,
158 // if we don't catch this and show it as "Running"
159 // it will flicker "Cancelled" briefly
160 return ProcessStatus.RUNNING;
162 case containerRequest.state === ContainerRequestState.FINAL &&
163 container?.state !== ContainerState.COMPLETE:
164 // Request was finalized before its container started (or the
165 // container was cancelled)
166 return ProcessStatus.CANCELLED;
168 case container && container.state === ContainerState.COMPLETE:
169 if (container?.exitCode === 0) {
170 if (containerRequest && container.finishedAt) {
171 // don't compare on createdAt because the container can
172 // have a slightly earlier creation time when it is created
173 // in the same transaction as the container request.
174 // use finishedAt because most people will assume "reused" means
175 // no additional work needed to be done, it's possible
176 // to share a running container but calling it "reused" in that case
177 // is more likely to just be confusing.
178 const finishedAt = new Date(container.finishedAt).getTime();
179 const createdAt = new Date(containerRequest.createdAt).getTime();
180 if (finishedAt < createdAt) {
181 return ProcessStatus.REUSED;
184 return ProcessStatus.COMPLETED;
186 return ProcessStatus.FAILED;
188 case container?.state === ContainerState.CANCELLED:
189 return ProcessStatus.CANCELLED;
191 case container?.state === ContainerState.QUEUED ||
192 container?.state === ContainerState.LOCKED:
193 if (containerRequest.priority === 0) {
194 return ProcessStatus.ONHOLD;
196 return ProcessStatus.QUEUED;
198 case container?.state === ContainerState.RUNNING:
199 if (container?.priority === 0) {
200 return ProcessStatus.CANCELLING;
202 if (!!container?.runtimeStatus.error) {
203 return ProcessStatus.FAILING;
205 if (!!container?.runtimeStatus.warning) {
206 return ProcessStatus.WARNING;
208 return ProcessStatus.RUNNING;
211 return ProcessStatus.UNKNOWN;
215 export const isProcessRunning = ({ container }: Process): boolean => (
216 container?.state === ContainerState.RUNNING
219 export const isProcessRunnable = ({ containerRequest }: Process): boolean => (
220 containerRequest.state === ContainerRequestState.UNCOMMITTED
223 export const isProcessResumable = ({ containerRequest, container }: Process): boolean => (
224 containerRequest.state === ContainerRequestState.COMMITTED &&
225 containerRequest.priority === 0 &&
226 // Don't show run button when container is present & running or cancelled
227 !(container && (container.state === ContainerState.RUNNING ||
228 container.state === ContainerState.CANCELLED ||
229 container.state === ContainerState.COMPLETE))
232 export const isProcessCancelable = ({ containerRequest, container }: Process): boolean => (
233 containerRequest.priority !== null &&
234 containerRequest.priority > 0 &&
235 container !== undefined &&
236 (container.state === ContainerState.QUEUED ||
237 container.state === ContainerState.LOCKED ||
238 container.state === ContainerState.RUNNING)
241 const isSubprocess = (containerUuid: string) => (resource: Resource) =>
242 resource.kind === ResourceKind.CONTAINER_REQUEST
243 && (resource as ContainerRequestResource).requestingContainerUuid === containerUuid;