Merge branch 'master' into 14099-process-service
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Wed, 29 Aug 2018 14:18:28 +0000 (16:18 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Wed, 29 Aug 2018 14:18:28 +0000 (16:18 +0200)
refs #14099

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

14 files changed:
src/components/details-attribute/details-attribute.tsx
src/models/container-request.ts
src/models/container.ts [new file with mode: 0644]
src/models/mount-types.ts [new file with mode: 0644]
src/models/resource.ts
src/models/runtime-constraints.ts [new file with mode: 0644]
src/models/scheduling-parameters.ts [new file with mode: 0644]
src/services/container-request-service/container-request-service.ts [new file with mode: 0644]
src/services/container-service/container-service.ts [new file with mode: 0644]
src/services/services.ts
src/store/processes/process.ts [new file with mode: 0644]
src/store/processes/processes-actions.ts [new file with mode: 0644]
src/store/resources/resources.ts
src/views-components/details-panel/process-details.tsx

index 4a94dc30d3f778a9a559631ada8026c93f561c68..fd8e948ff046a5c14106e16ea426ff31b3807d4f 100644 (file)
@@ -41,7 +41,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 interface DetailsAttributeDataProps {
     label: string;
     classLabel?: string;
-    value?: string | number;
+    value?: React.ReactNode;
     classValue?: string;
     lowercaseValue?: boolean;
     link?: string;
index d1bcc36c81e9a548e34006c9696e47340a422002..78891c7bdcd8fc23918ff369052548dd02b725db 100644 (file)
@@ -3,6 +3,9 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { Resource, ResourceKind } from "./resource";
+import { MountType } from "~/models/mount-types";
+import { RuntimeConstraints } from './runtime-constraints';
+import { SchedulingParameters } from './scheduling-parameters';
 
 export enum ContainerRequestState {
     UNCOMMITTED = "Uncommitted",
@@ -16,12 +19,12 @@ export interface ContainerRequestResource extends Resource {
     description: string;
     properties: any;
     state: ContainerRequestState;
-    requestingContainerUuid: string;
-    containerUuid: string;
+    requestingContainerUuid: string | null;
+    containerUuid: string | null;
     containerCountMax: number;
-    mounts: any;
-    runtimeConstraints: any;
-    schedulingParameters: any;
+    mounts: MountType[];
+    runtimeConstraints: RuntimeConstraints;
+    schedulingParameters: SchedulingParameters;
     containerImage: string;
     environment: any;
     cwd: string;
@@ -29,10 +32,10 @@ export interface ContainerRequestResource extends Resource {
     outputPath: string;
     outputName: string;
     outputTtl: number;
-    priority: number;
+    priority: number | null;
     expiresAt: string;
     useExisting: boolean;
-    logUuid: string;
-    outputUuid: string;
+    logUuid: string | null;
+    outputUuid: string | null;
     filters: string;
 }
diff --git a/src/models/container.ts b/src/models/container.ts
new file mode 100644 (file)
index 0000000..99cb309
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Resource, ResourceKind } from "./resource";
+import { MountType } from '~/models/mount-types';
+import { RuntimeConstraints } from "~/models/runtime-constraints";
+import { SchedulingParameters } from './scheduling-parameters';
+
+export enum ContainerState {
+    QUEUED = 'Queued',
+    LOCKED = 'Locked',
+    RUNNING = 'Running',
+    COMPLETE = 'Complete',
+    CANCELLED = 'Cancelled',
+}
+
+export interface ContainerResource extends Resource {
+    kind: ResourceKind.CONTAINER;
+    state: string;
+    startedAt: string | null;
+    finishedAt: string | null;
+    log: string | null;
+    environment: {};
+    cwd: string;
+    command: string[];
+    outputPath: string;
+    mounts: MountType[];
+    runtimeConstraints: RuntimeConstraints;
+    schedulingParameters: SchedulingParameters;
+    output: string | null;
+    containerImage: string;
+    progress: number;
+    priority: number;
+    exitCode: number | null;
+    authUuid: string | null;
+    lockedByUuid: string | null;
+}
diff --git a/src/models/mount-types.ts b/src/models/mount-types.ts
new file mode 100644 (file)
index 0000000..ec48c85
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export enum MountKind {
+    COLLECTION = 'collection',
+    GIT_TREE = 'git_tree',
+    TEMPORARY_DIRECTORY = 'tmp',
+    KEEP = 'keep',
+    MOUNTED_FILE = 'file',
+    JSON = 'JSON'
+}
+
+export type MountType =
+    CollectionMount |
+    GitTreeMount |
+    TemporaryDirectoryMount |
+    KeepMount |
+    JSONMount;
+
+export interface CollectionMount {
+    kind: MountKind.COLLECTION;
+    uuid?: string;
+    portableDataHash?: string;
+    path?: string;
+    writable?: boolean;
+}
+
+export interface GitTreeMount {
+    kind: MountKind.GIT_TREE;
+    uuid: string;
+    commit: string;
+    path?: string;
+}
+
+export enum TemporaryDirectoryDeviceType {
+    RAM = 'ram',
+    SSD = 'ssd',
+    DISK = 'disk',
+    NETWORK = 'network',
+}
+
+export interface TemporaryDirectoryMount {
+    kind: MountKind.TEMPORARY_DIRECTORY;
+    capacity: number;
+    deviceType: TemporaryDirectoryDeviceType;
+}
+
+export interface KeepMount {
+    kind: MountKind.KEEP;
+}
+
+export interface JSONMount {
+    kind: MountKind.JSON;
+    content: string;
+}
index ff95c1a9b8df59872d6819dccdbcfb2b8ee92441..3290bdfe06f07ed60fa27accd067cff47f0d0b6b 100644 (file)
@@ -16,18 +16,21 @@ export interface Resource {
 
 export enum ResourceKind {
     COLLECTION = "arvados#collection",
+    CONTAINER = "arvados#container",
     CONTAINER_REQUEST = "arvados#containerRequest",
     GROUP = "arvados#group",
     PROCESS = "arvados#containerRequest",
     PROJECT = "arvados#group",
-    WORKFLOW = "arvados#workflow",
     USER = "arvados#user",
+    WORKFLOW = "arvados#workflow",
 }
 
 export enum ResourceObjectType {
-    USER = 'tpzed',
+    COLLECTION = '4zz18',
+    CONTAINER = 'dz642',
+    CONTAINER_REQUEST = 'xvhdp',
     GROUP = 'j7d0g',
-    COLLECTION = '4zz18'
+    USER = 'tpzed',
 }
 
 export const RESOURCE_UUID_PATTERN = '.{5}-.{5}-.{15}';
@@ -52,6 +55,10 @@ export const extractUuidKind = (uuid: string = '') => {
             return ResourceKind.GROUP;
         case ResourceObjectType.COLLECTION:
             return ResourceKind.COLLECTION;
+        case ResourceObjectType.CONTAINER_REQUEST:
+            return ResourceKind.CONTAINER_REQUEST;
+        case ResourceObjectType.CONTAINER:
+            return ResourceKind.CONTAINER;
         default:
             return undefined;
     }
diff --git a/src/models/runtime-constraints.ts b/src/models/runtime-constraints.ts
new file mode 100644 (file)
index 0000000..ba90537
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export interface RuntimeConstraints {
+    ram: number;
+    vcpus: number;
+    keepCacheRam: number;
+    API: boolean;
+}
diff --git a/src/models/scheduling-parameters.ts b/src/models/scheduling-parameters.ts
new file mode 100644 (file)
index 0000000..62f7224
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export interface SchedulingParameters {
+    partitions: string[];
+    preemptible: boolean;
+    maxRunTime: number;
+}
diff --git a/src/services/container-request-service/container-request-service.ts b/src/services/container-request-service/container-request-service.ts
new file mode 100644 (file)
index 0000000..8cf8e74
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { CommonResourceService } from "~/common/api/common-resource-service";
+import { AxiosInstance } from "axios";
+import { ContainerRequestResource } from '../../models/container-request';
+
+export class ContainerRequestService extends CommonResourceService<ContainerRequestResource> {
+    constructor(serverApi: AxiosInstance) {
+        super(serverApi, "container_requests");
+    }
+}
diff --git a/src/services/container-service/container-service.ts b/src/services/container-service/container-service.ts
new file mode 100644 (file)
index 0000000..698c7f5
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { CommonResourceService } from "~/common/api/common-resource-service";
+import { AxiosInstance } from "axios";
+import { ContainerResource } from '../../models/container';
+
+export class ContainerService extends CommonResourceService<ContainerResource> {
+    constructor(serverApi: AxiosInstance) {
+        super(serverApi, "containers");
+    }
+}
index 6295527bfb40d5c326b56a4a4d09dfe27d22e85e..32e7bd18b2ff9bd54ee1305af28e132e5da4cfe9 100644 (file)
@@ -17,6 +17,8 @@ import { Config } from "../common/config";
 import { UserService } from './user-service/user-service';
 import { AncestorService } from "~/services/ancestors-service/ancestors-service";
 import { ResourceKind } from "~/models/resource";
+import { ContainerRequestService } from './container-request-service/container-request-service';
+import { ContainerService } from './container-service/container-service';
 
 export type ServiceRepository = ReturnType<typeof createServices>;
 
@@ -27,32 +29,37 @@ export const createServices = (config: Config) => {
     const webdavClient = new WebDAV();
     webdavClient.defaults.baseURL = config.keepWebServiceUrl;
 
-    const authService = new AuthService(apiClient, config.rootUrl);
-    const keepService = new KeepService(apiClient);
     const groupsService = new GroupsService(apiClient);
-    const projectService = new ProjectService(apiClient);
+    const keepService = new KeepService(apiClient);
     const linkService = new LinkService(apiClient);
-    const favoriteService = new FavoriteService(linkService, groupsService);
-    const collectionService = new CollectionService(apiClient, webdavClient, authService);
-    const tagService = new TagService(linkService);
-    const collectionFilesService = new CollectionFilesService(collectionService);
+    const projectService = new ProjectService(apiClient);
     const userService = new UserService(apiClient);
+    const containerRequestService = new ContainerRequestService(apiClient);
+    const containerService = new ContainerService(apiClient);
+    
     const ancestorsService = new AncestorService(groupsService, userService);
+    const authService = new AuthService(apiClient, config.rootUrl);
+    const collectionService = new CollectionService(apiClient, webdavClient, authService);
+    const collectionFilesService = new CollectionFilesService(collectionService);
+    const favoriteService = new FavoriteService(linkService, groupsService);
+    const tagService = new TagService(linkService);
 
     return {
+        ancestorsService,
         apiClient,
-        webdavClient,
         authService,
-        keepService,
+        collectionFilesService,
+        collectionService,
+        containerRequestService,
+        containerService,
+        favoriteService,
         groupsService,
-        projectService,
+        keepService,
         linkService,
-        favoriteService,
-        collectionService,
+        projectService,
         tagService,
-        collectionFilesService,
         userService,
-        ancestorsService,
+        webdavClient,
     };
 };
 
diff --git a/src/store/processes/process.ts b/src/store/processes/process.ts
new file mode 100644 (file)
index 0000000..46d8a25
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// 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 { filterResources } from '../resources/resources';
+import { ResourceKind, Resource } from '~/models/resource';
+
+export interface Process {
+    containerRequest: ContainerRequestResource;
+    container?: ContainerResource;
+}
+
+export const getProcess = (uuid: string) => (resources: ResourcesState): Process | undefined => {
+    const containerRequest = getResource<ContainerRequestResource>(uuid)(resources);
+    if (containerRequest) {
+        if (containerRequest.containerUuid) {
+            const container = getResource<ContainerResource>(containerRequest.containerUuid)(resources);
+            if (container) {
+                return { containerRequest, container };
+            }
+        }
+        return { containerRequest };
+    }
+    return;
+};
+
+export const getSubprocesses = (uuid: string) => (resources: ResourcesState) => {
+    const containerRequests = filterResources(isSubprocess(uuid))(resources) as ContainerRequestResource[];
+    return containerRequests.reduce((subprocesses, { uuid }) => {
+        const process = getProcess(uuid)(resources);
+        return process
+            ? [...subprocesses, process]
+            : subprocesses;
+    }, []);
+};
+
+export const getProcessStatus = (process: Process) =>
+    process.container
+        ? process.container.state
+        : process.containerRequest.state;
+
+const isSubprocess = (uuid: string) => (resource: Resource) =>
+    resource.kind === ResourceKind.CONTAINER_REQUEST
+    && (resource as ContainerRequestResource).requestingContainerUuid === uuid;
diff --git a/src/store/processes/processes-actions.ts b/src/store/processes/processes-actions.ts
new file mode 100644 (file)
index 0000000..d667517
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { updateResources } from '~/store/resources/resources-actions';
+import { FilterBuilder } from '~/common/api/filter-builder';
+import { ContainerRequestResource } from '../../models/container-request';
+
+export const loadProcess = (uuid: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const containerRequest = await services.containerRequestService.get(uuid);
+        dispatch<any>(updateResources([containerRequest]));
+        if (containerRequest.containerUuid) {
+            const container = await services.containerService.get(containerRequest.containerUuid);
+            dispatch<any>(updateResources([container]));
+        }
+    };
+
+export const loadSubprocesses = (uuid: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const containerRequests = await dispatch<any>(loadContainerRequests(
+            new FilterBuilder().addEqual('requestingContainerUuid', uuid).getFilters()
+        )) as ContainerRequestResource[];
+
+        const containerUuids: string[] = containerRequests.reduce((uuids, { containerUuid }) =>
+            containerUuid
+                ? [...uuids, containerUuid]
+                : uuids, []);
+
+        if (containerUuids.length > 0) {
+            await dispatch<any>(loadContainers(
+                new FilterBuilder().addIn('uuid', containerUuids).getFilters()
+            ));
+        }
+    };
+
+export const loadContainerRequests = (filters: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const { items } = await services.containerRequestService.list({ filters });
+        dispatch<any>(updateResources(items));
+        return items;
+    };
+
+export const loadContainers = (filters: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const { items } = await services.containerService.list({ filters });
+        dispatch<any>(updateResources(items));
+        return items;
+    };
index add4efef54b4cd4d63178e0239927f4619587160..10c82ffe7f36bbd13c7ac8c193b6f1a182c94eac 100644 (file)
@@ -19,7 +19,7 @@ export const setResource = <T extends Resource>(id: string, data: T) =>
 
 export const deleteResource = (id: string) =>
     (state: ResourcesState) => {
-        const newState = {...state};
+        const newState = { ...state };
         delete newState[id];
         return newState;
     };
@@ -28,10 +28,14 @@ export const filterResources = (filter: (resource: Resource) => boolean) =>
     (state: ResourcesState) =>
         Object
             .keys(state)
-            .map(id => getResource(id)(state))
+            .reduce((resources, id) => {
+                const resource = getResource(id)(state);
+                return resource
+                    ? [...resources, resource]
+                    : resources;
+            }, [])
             .filter(filter);
 
 export const filterResourcesByKind = (kind: ResourceKind) =>
     (state: ResourcesState) =>
         filterResources(resource => resource.kind === kind)(state);
-
index cec01b966d91ecd5eb26dcda42779c579915dc49..d9ddfad89ff6579ca382fd5bcd90e5a19233a652 100644 (file)
@@ -34,7 +34,7 @@ export class ProcessDetails extends DetailsData<ProcessResource> {
             {/* Links but we dont have view */}
             <DetailsAttribute label='Outputs' link={this.item.outputPath} value={this.item.outputPath} />
             <DetailsAttribute label='UUID' link={this.item.uuid} value={this.item.uuid} />
-            <DetailsAttribute label='Container UUID' link={this.item.containerUuid} value={this.item.containerUuid} />
+            <DetailsAttribute label='Container UUID' link={this.item.containerUuid || ''} value={this.item.containerUuid} />
 
             <DetailsAttribute label='Priority' value={this.item.priority} />
             <DetailsAttribute label='Runtime Constraints' value={this.item.runtimeConstraints} />