[arvados-workbench2] updated: 2.6.3-58-g5a0fee4d

git repository hosting git at public.arvados.org
Mon Aug 21 16:36:23 UTC 2023


Summary of changes:
 cypress/support/commands.js                        |  26 ++--
 src/services/log-service/log-service.test.ts       | 168 +++++++++++++++++++++
 src/services/log-service/log-service.ts            |  18 ++-
 .../process-logs-panel-actions.ts                  |   5 +-
 4 files changed, 198 insertions(+), 19 deletions(-)
 create mode 100644 src/services/log-service/log-service.test.ts

       via  5a0fee4d900804c50149cc87376e0bc65a390985 (commit)
       via  cf44d56c363942f97041e475e4bb29d8918482a3 (commit)
       via  d5e01ee547a97d506828962823cdaa6445869a18 (commit)
       via  0ab0f06d5f26c28b58a4e4fa6bc91753cccefd81 (commit)
       via  d4e77383fbd444a12152e575688b45c369104d53 (commit)
      from  59cc53cbed401e6b5ad750d992f64a8f5c83c6bf (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 5a0fee4d900804c50149cc87376e0bc65a390985
Author: Stephen Smith <stephen at curii.com>
Date:   Mon Aug 21 12:35:59 2023 -0400

    20219: Suppress error toast when process logs fail to load due to 404
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/store/process-logs-panel/process-logs-panel-actions.ts b/src/store/process-logs-panel/process-logs-panel-actions.ts
index 844f0154..87a2fa12 100644
--- a/src/store/process-logs-panel/process-logs-panel-actions.ts
+++ b/src/store/process-logs-panel/process-logs-panel-actions.ts
@@ -61,7 +61,10 @@ export const initProcessLogsPanel = (processUuid: string) =>
             // On error, populate empty state to allow polling to start
             const initialState = createInitialLogPanelState([], []);
             dispatch(processLogsPanelActions.INIT_PROCESS_LOGS_PANEL(initialState));
-            dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not load process logs', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+            // Only show toast on errors other than 404 since 404 is expected when logs do not exist yet
+            if (e.status !== 404) {
+                dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not load process logs', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+            }
         }
     };
 

commit cf44d56c363942f97041e475e4bb29d8918482a3
Author: Stephen Smith <stephen at curii.com>
Date:   Mon Aug 21 12:31:27 2023 -0400

    20219: Add unit tests for log service webdav methods
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/services/log-service/log-service.test.ts b/src/services/log-service/log-service.test.ts
new file mode 100644
index 00000000..2519155b
--- /dev/null
+++ b/src/services/log-service/log-service.test.ts
@@ -0,0 +1,168 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { LogService } from "./log-service";
+import { ApiActions } from "services/api/api-actions";
+import axios from "axios";
+import { WebDAVRequestConfig } from "common/webdav";
+import { LogEventType } from "models/log";
+
+describe("LogService", () => {
+
+    let apiWebdavClient: any;
+    const axiosInstance = axios.create();
+    const actions: ApiActions = {
+        progressFn: (id: string, working: boolean) => {},
+        errorFn: (id: string, message: string) => {}
+    };
+
+    beforeEach(() => {
+        apiWebdavClient = {
+            delete: jest.fn(),
+            upload: jest.fn(),
+            mkdir: jest.fn(),
+            get: jest.fn(),
+            propfind: jest.fn(),
+        } as any;
+    });
+
+    it("lists log files using propfind on live logs api endpoint", async () => {
+        const logService = new LogService(axiosInstance, apiWebdavClient, actions);
+
+        // given
+        const containerRequest = {uuid: 'zzzzz-xvhdp-000000000000000', containerUuid: 'zzzzz-dz642-000000000000000'};
+        const xmlData = `<?xml version="1.0" encoding="UTF-8"?>
+            <D:multistatus xmlns:D="DAV:">
+                    <D:response>
+                            <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/</D:href>
+                            <D:propstat>
+                                    <D:prop>
+                                            <D:resourcetype>
+                                                    <D:collection xmlns:D="DAV:" />
+                                            </D:resourcetype>
+                                            <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
+                                            <D:displayname></D:displayname>
+                                            <D:supportedlock>
+                                                    <D:lockentry xmlns:D="DAV:">
+                                                            <D:lockscope>
+                                                                    <D:exclusive />
+                                                            </D:lockscope>
+                                                            <D:locktype>
+                                                                    <D:write />
+                                                            </D:locktype>
+                                                    </D:lockentry>
+                                            </D:supportedlock>
+                                    </D:prop>
+                                    <D:status>HTTP/1.1 200 OK</D:status>
+                            </D:propstat>
+                    </D:response>
+                    <D:response>
+                            <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/stdout.txt</D:href>
+                            <D:propstat>
+                                    <D:prop>
+                                            <D:displayname>stdout.txt</D:displayname>
+                                            <D:getcontentlength>15</D:getcontentlength>
+                                            <D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
+                                            <D:getetag>"177b8fb161ff9f58f"</D:getetag>
+                                            <D:supportedlock>
+                                                    <D:lockentry xmlns:D="DAV:">
+                                                            <D:lockscope>
+                                                                    <D:exclusive />
+                                                            </D:lockscope>
+                                                            <D:locktype>
+                                                                    <D:write />
+                                                            </D:locktype>
+                                                    </D:lockentry>
+                                            </D:supportedlock>
+                                            <D:resourcetype></D:resourcetype>
+                                            <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
+                                    </D:prop>
+                                    <D:status>HTTP/1.1 200 OK</D:status>
+                            </D:propstat>
+                    </D:response>
+                    <D:response>
+                            <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/wrongpath.txt</D:href>
+                            <D:propstat>
+                                    <D:prop>
+                                            <D:displayname>wrongpath.txt</D:displayname>
+                                            <D:getcontentlength>15</D:getcontentlength>
+                                            <D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
+                                            <D:getetag>"177b8fb161ff9f58f"</D:getetag>
+                                            <D:supportedlock>
+                                                    <D:lockentry xmlns:D="DAV:">
+                                                            <D:lockscope>
+                                                                    <D:exclusive />
+                                                            </D:lockscope>
+                                                            <D:locktype>
+                                                                    <D:write />
+                                                            </D:locktype>
+                                                    </D:lockentry>
+                                            </D:supportedlock>
+                                            <D:resourcetype></D:resourcetype>
+                                            <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
+                                    </D:prop>
+                                    <D:status>HTTP/1.1 200 OK</D:status>
+                            </D:propstat>
+                    </D:response>
+            </D:multistatus>`;
+        const xmlDoc = (new DOMParser()).parseFromString(xmlData, "text/xml");
+        apiWebdavClient.propfind = jest.fn().mockReturnValue(Promise.resolve({responseXML: xmlDoc}));
+
+        // when
+        const logs = await logService.listLogFiles(containerRequest);
+
+        // then
+        expect(apiWebdavClient.propfind).toHaveBeenCalledWith(`container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}`);
+        expect(logs.length).toEqual(1);
+        expect(logs[0]).toHaveProperty('name', 'stdout.txt');
+        expect(logs[0]).toHaveProperty('type', 'file');
+    });
+
+    it("requests log file contents with correct range request", async () => {
+        const logService = new LogService(axiosInstance, apiWebdavClient, actions);
+
+        // given
+        const containerRequest = {uuid: 'zzzzz-xvhdp-000000000000000', containerUuid: 'zzzzz-dz642-000000000000000'};
+        const fileRecord = {name: `stdout.txt`};
+        const fileContents = `Line 1\nLine 2\nLine 3`;
+        apiWebdavClient.get = jest.fn().mockImplementation((path: string, options: WebDAVRequestConfig) => {
+            const matches = /bytes=([0-9]+)-([0-9]+)/.exec(options.headers?.Range || '');
+            if (matches?.length === 3) {
+                return Promise.resolve({responseText: fileContents.substring(Number(matches[1]), Number(matches[2]) + 1)})
+            }
+            return Promise.reject();
+        });
+
+        // when
+        let result = await logService.getLogFileContents(containerRequest, fileRecord, 0, 3);
+        // then
+        expect(apiWebdavClient.get).toHaveBeenCalledWith(
+            `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
+            {headers: {Range: `bytes=0-3`}}
+        );
+        expect(result.logType).toEqual(LogEventType.STDOUT);
+        expect(result.contents).toEqual(['Line']);
+
+        // when
+        result = await logService.getLogFileContents(containerRequest, fileRecord, 0, 10);
+        // then
+        expect(apiWebdavClient.get).toHaveBeenCalledWith(
+            `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
+            {headers: {Range: `bytes=0-10`}}
+        );
+        expect(result.logType).toEqual(LogEventType.STDOUT);
+        expect(result.contents).toEqual(['Line 1', 'Line']);
+
+        // when
+        result = await logService.getLogFileContents(containerRequest, fileRecord, 6, 14);
+        // then
+        expect(apiWebdavClient.get).toHaveBeenCalledWith(
+            `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
+            {headers: {Range: `bytes=6-14`}}
+        );
+        expect(result.logType).toEqual(LogEventType.STDOUT);
+        expect(result.contents).toEqual(['', 'Line 2', 'L']);
+    });
+
+});

commit d5e01ee547a97d506828962823cdaa6445869a18
Author: Stephen Smith <stephen at curii.com>
Date:   Mon Aug 21 12:30:57 2023 -0400

    20219: Make log service easier to test using interfaces
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/services/log-service/log-service.ts b/src/services/log-service/log-service.ts
index f93a3f81..f36044f4 100644
--- a/src/services/log-service/log-service.ts
+++ b/src/services/log-service/log-service.ts
@@ -21,9 +21,9 @@ export class LogService extends CommonResourceService<LogResource> {
         super(serverApi, "logs", actions);
     }
 
-    async listLogFiles(containerRequest: ContainerRequestResource) {
+    async listLogFiles(containerRequest: Pick<ContainerRequestResource, 'uuid' | 'containerUuid'>) {
         const request = await this.apiWebdavClient.propfind(`container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}`);
-        if (request.responseXML != null) {
+        if (request?.responseXML != null) {
             return extractFilesData(request.responseXML)
                 .filter((file) => (
                     file.path === `/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}`
@@ -32,14 +32,22 @@ export class LogService extends CommonResourceService<LogResource> {
         return Promise.reject();
     }
 
-    async getLogFileContents(containerRequest: ContainerRequestResource, fileRecord: CollectionFile, startByte: number, endByte: number): Promise<LogFragment> {
+    /**
+     * Fetches the specified log file contents from the given container request's container live logs endpoint
+     * @param containerRequest Container request to fetch logs for
+     * @param fileRecord Log file to fetch
+     * @param startByte First byte index of the log file to fetch
+     * @param endByte Last byte index to include in the response
+     * @returns A promise that resolves to the LogEventType and a string array of the log file contents
+     */
+    async getLogFileContents(containerRequest: Pick<ContainerRequestResource, 'uuid' | 'containerUuid'>, fileRecord: Pick<CollectionFile, 'name'>, startByte: number, endByte: number): Promise<LogFragment> {
         const request = await this.apiWebdavClient.get(
             `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
             {headers: {Range: `bytes=${startByte}-${endByte}`}}
         );
         const logFileType = logFileToLogType(fileRecord);
 
-        if (request.responseText && logFileType) {
+        if (request?.responseText && logFileType) {
             return {
                 logType: logFileType,
                 contents: request.responseText.split(/\r?\n/),
@@ -50,4 +58,4 @@ export class LogService extends CommonResourceService<LogResource> {
     }
 }
 
-export const logFileToLogType = (file: CollectionFile) => (file.name.replace(/\.(txt|json)$/, '') as LogEventType);
+export const logFileToLogType = (file: Pick<CollectionFile, 'name'>) => (file.name.replace(/\.(txt|json)$/, '') as LogEventType);

commit 0ab0f06d5f26c28b58a4e4fa6bc91753cccefd81
Author: Stephen Smith <stephen at curii.com>
Date:   Mon Aug 21 12:30:23 2023 -0400

    20219: Make cypress collection replace files method more specific
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 2bb2ec27..fadd73e0 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -196,7 +196,12 @@ Cypress.Commands.add(
 
 Cypress.Commands.add(
     "collectionReplaceFiles", (token, uuid, data) => {
-        return cy.updateResource(token, 'collections', uuid, JSON.stringify(data))
+        return cy.updateResource(token, 'collections', uuid, {
+            collection: {
+                preserve_version: true,
+            },
+            replace_files: JSON.stringify(data)
+        })
     }
 )
 
@@ -286,12 +291,7 @@ Cypress.Commands.add(
                     }).then(() => (
                         // Create empty directory for container uuid
                         cy.collectionReplaceFiles(token, collection.uuid, {
-                            collection: {
-                                preserve_version: true,
-                            },
-                            replace_files: {
-                                [`/${containerLogFolderPrefix}${containerRequest.container_uuid}`]: "d41d8cd98f00b204e9800998ecf8427e+0"
-                            }
+                            [`/${containerLogFolderPrefix}${containerRequest.container_uuid}`]: "d41d8cd98f00b204e9800998ecf8427e+0"
                         }).then(() => (
                             // Put new log file with contents into fake log collection
                             cy.doWebDAVRequest(

commit d4e77383fbd444a12152e575688b45c369104d53
Author: Stephen Smith <stephen at curii.com>
Date:   Mon Aug 21 12:22:41 2023 -0400

    20219: Rename doKeepRequest to doWebDAVRequest for clarity
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 24629d99..2bb2ec27 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -65,7 +65,7 @@ Cypress.Commands.add(
 });
 
 Cypress.Commands.add(
-    "doKeepRequest", (method = 'GET', path = '', data = null, qs = null,
+    "doWebDAVRequest", (method = 'GET', path = '', data = null, qs = null,
         token = systemToken, auth = false, followRedirect = true, failOnStatusCode = true) => {
     return cy.doRequest('GET', '/arvados/v1/config', null, null).then(({body: config}) => {
         return cy.request({
@@ -248,14 +248,14 @@ Cypress.Commands.add(
                     const filePath = `${containerRequest.log_uuid}/${containerLogFolderPrefix}${containerRequest.container_uuid}/${fileName}`;
                     if (logFiles.find((file) => (file.name === fileName))) {
                         // File exists, fetch and append
-                        return cy.doKeepRequest(
+                        return cy.doWebDAVRequest(
                                 "GET",
                                 `c=${filePath}`,
                                 null,
                                 null,
                                 token
                             )
-                            .then(({ body: contents }) => cy.doKeepRequest(
+                            .then(({ body: contents }) => cy.doWebDAVRequest(
                                 "PUT",
                                 `c=${filePath}`,
                                 contents.split("\n").concat(lines).join("\n"),
@@ -264,7 +264,7 @@ Cypress.Commands.add(
                             ));
                     } else {
                         // File not exists, put new file
-                        cy.doKeepRequest(
+                        cy.doWebDAVRequest(
                             "PUT",
                             `c=${filePath}`,
                             lines.join("\n"),
@@ -294,7 +294,7 @@ Cypress.Commands.add(
                             }
                         }).then(() => (
                             // Put new log file with contents into fake log collection
-                            cy.doKeepRequest(
+                            cy.doWebDAVRequest(
                                 'PUT',
                                 `c=${collection.uuid}/${containerLogFolderPrefix}${containerRequest.container_uuid}/${fileName}`,
                                 lines.join('\n'),
@@ -312,7 +312,7 @@ Cypress.Commands.add(
 Cypress.Commands.add(
     "listContainerRequestLogs", (token, crUuid) => (
         cy.getContainerRequest(token, crUuid).then((containerRequest) => (
-            cy.doKeepRequest('PROPFIND', `c=${containerRequest.log_uuid}/${containerLogFolderPrefix}${containerRequest.container_uuid}`, null, null, token)
+            cy.doWebDAVRequest('PROPFIND', `c=${containerRequest.log_uuid}/${containerLogFolderPrefix}${containerRequest.container_uuid}`, null, null, token)
                 .then(({body: data}) => {
                     return extractFilesData(new DOMParser().parseFromString(data, "text/xml"));
                 })

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list