X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/2f2b2fe9fedb03e78c1ff520681ffce4715ff760..58afb8dfcfa3fefa8e1b28dc418b97c51a73b7dc:/src/store/processes/process.ts diff --git a/src/store/processes/process.ts b/src/store/processes/process.ts index 467fc8a92e..526629cd02 100644 --- a/src/store/processes/process.ts +++ b/src/store/processes/process.ts @@ -2,30 +2,46 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { ContainerRequestResource } from '../../models/container-request'; -import { ContainerResource } from '../../models/container'; -import { ResourcesState, getResource } from '~/store/resources/resources'; +import { ContainerRequestResource, ContainerRequestState } from '../../models/container-request'; +import { ContainerResource, ContainerState } from '../../models/container'; +import { ResourcesState, getResource } from 'store/resources/resources'; import { filterResources } from '../resources/resources'; -import { ResourceKind, Resource } from '~/models/resource'; -import { getTimeDiff } from '~/common/formatters'; -import { SubprocessesStatus } from '~/views/process-panel/process-subprocesses-card'; -import { ArvadosTheme } from '~/common/custom-theme'; +import { ResourceKind, Resource, extractUuidKind } from 'models/resource'; +import { getTimeDiff } from 'common/formatters'; +import { ArvadosTheme } from 'common/custom-theme'; export interface Process { containerRequest: ContainerRequestResource; container?: ContainerResource; } +export enum ProcessStatus { + CANCELLED = 'Cancelled', + COMPLETED = 'Completed', + DRAFT = 'Draft', + FAILING = 'Failing', + FAILED = 'Failed', + ONHOLD = 'On hold', + QUEUED = 'Queued', + RUNNING = 'Running', + WARNING = 'Warning', + UNKNOWN = 'Unknown', + REUSED = 'Reused', + CANCELLING = 'Cancelling', +} + export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => { - const containerRequest = getResource(uuid)(resources); - if (containerRequest) { - if (containerRequest.containerUuid) { - const container = getResource(containerRequest.containerUuid)(resources); - if (container) { - return { containerRequest, container }; + if (extractUuidKind(uuid) === ResourceKind.CONTAINER_REQUEST) { + const containerRequest = getResource(uuid)(resources); + if (containerRequest) { + if (containerRequest.containerUuid) { + const container = getResource(containerRequest.containerUuid)(resources); + if (container) { + return { containerRequest, container }; + } } + return { containerRequest }; } - return { containerRequest }; } return; }; @@ -44,34 +60,157 @@ export const getSubprocesses = (uuid: string) => (resources: ResourcesState) => return []; }; -export const getProcessRuntime = ({ container }: Process) => - container - ? getTimeDiff(container.finishedAt || '', container.startedAt || '') - : 0; +export const getProcessRuntime = ({ container }: Process) => { + if (container) { + if (container.startedAt === null) { + return 0; + } + if (container.finishedAt === null) { + // Count it from now + return new Date().getTime() - new Date(container.startedAt).getTime(); + } + return getTimeDiff(container.finishedAt, container.startedAt); + } else { + return 0; + } +}; + -export const getProcessStatusColor = (status: string, { customs }: ArvadosTheme) => { +export const getProcessStatusStyles = (status: string, theme: ArvadosTheme): React.CSSProperties => { + let color = theme.customs.colors.grey500; + let running = false; switch (status) { - case SubprocessesStatus.COMPLETED: - return customs.colors.green700; - case SubprocessesStatus.CANCELED: - return customs.colors.red900; - case SubprocessesStatus.QUEUED: - return customs.colors.grey500; - case SubprocessesStatus.FAILED: - return customs.colors.red900; - case SubprocessesStatus.ACTIVE: - return customs.colors.blue500; + case ProcessStatus.RUNNING: + color = theme.customs.colors.green800; + running = true; + break; + case ProcessStatus.COMPLETED: + case ProcessStatus.REUSED: + color = theme.customs.colors.green800; + break; + case ProcessStatus.WARNING: + color = theme.customs.colors.green800; + running = true; + break; + case ProcessStatus.FAILING: + color = theme.customs.colors.red900; + running = true; + break; + case ProcessStatus.CANCELLING: + color = theme.customs.colors.red900; + running = true; + break; + case ProcessStatus.CANCELLED: + case ProcessStatus.FAILED: + color = theme.customs.colors.red900; + break; + case ProcessStatus.QUEUED: + color = theme.customs.colors.grey600; + running = true; + break; + default: + color = theme.customs.colors.grey600; + break; + } + + // Using color and running we build the text, border, and background style properties + return { + // Set background color when not running, otherwise use white + backgroundColor: running ? theme.palette.common.white : color, + // Set text color to status color when running, else use white text for solid button + color: running ? color : theme.palette.common.white, + // Set border color when running, else omit the style entirely + ...(running ? { border: `2px solid ${color}` } : {}), + }; +}; + +export const getProcessStatus = ({ containerRequest, container }: Process): ProcessStatus => { + switch (true) { + case containerRequest.containerUuid && !container: + return ProcessStatus.UNKNOWN; + + case containerRequest.state === ContainerRequestState.FINAL && + container?.state !== ContainerState.COMPLETE: + // Request was finalized before its container started (or the + // container was cancelled) + return ProcessStatus.CANCELLED; + + case containerRequest.state === ContainerRequestState.UNCOMMITTED: + return ProcessStatus.DRAFT; + + case container && container.state === ContainerState.COMPLETE: + if (container?.exitCode === 0) { + if (containerRequest && container.finishedAt) { + // don't compare on createdAt because the container can + // have a slightly earlier creation time when it is created + // in the same transaction as the container request. + // use finishedAt because most people will assume "reused" means + // no additional work needed to be done, it's possible + // to share a running container but calling it "reused" in that case + // is more likely to just be confusing. + const finishedAt = new Date(container.finishedAt).getTime(); + const createdAt = new Date(containerRequest.createdAt).getTime(); + if (finishedAt < createdAt) { + return ProcessStatus.REUSED; + } + } + return ProcessStatus.COMPLETED; + } + return ProcessStatus.FAILED; + + case container?.state === ContainerState.CANCELLED: + return ProcessStatus.CANCELLED; + + case container?.state === ContainerState.QUEUED || + container?.state === ContainerState.LOCKED: + if (containerRequest.priority === 0) { + return ProcessStatus.ONHOLD; + } + return ProcessStatus.QUEUED; + + case container?.state === ContainerState.RUNNING: + if (container?.priority === 0) { + return ProcessStatus.CANCELLING; + } + if (!!container?.runtimeStatus.error) { + return ProcessStatus.FAILING; + } + if (!!container?.runtimeStatus.warning) { + return ProcessStatus.WARNING; + } + return ProcessStatus.RUNNING; + default: - return customs.colors.grey500; + return ProcessStatus.UNKNOWN; } }; -export const getProcessStatus = (process: Process) => - process.container - ? process.container.state - : process.containerRequest.state; +export const isProcessRunning = ({ container }: Process): boolean => ( + container?.state === ContainerState.RUNNING +); + +export const isProcessRunnable = ({ containerRequest }: Process): boolean => ( + containerRequest.state === ContainerRequestState.UNCOMMITTED +); + +export const isProcessResumable = ({ containerRequest, container }: Process): boolean => ( + containerRequest.state === ContainerRequestState.COMMITTED && + containerRequest.priority === 0 && + // Don't show run button when container is present & running or cancelled + !(container && (container.state === ContainerState.RUNNING || + container.state === ContainerState.CANCELLED || + container.state === ContainerState.COMPLETE)) +); + +export const isProcessCancelable = ({ containerRequest, container }: Process): boolean => ( + containerRequest.priority !== null && + containerRequest.priority > 0 && + container !== undefined && + (container.state === ContainerState.QUEUED || + container.state === ContainerState.LOCKED || + container.state === ContainerState.RUNNING) +); const isSubprocess = (containerUuid: string) => (resource: Resource) => resource.kind === ResourceKind.CONTAINER_REQUEST && (resource as ContainerRequestResource).requestingContainerUuid === containerUuid; -