This commit is contained in:
Alexander Rose
2024-08-24 15:01:12 -07:00
10 changed files with 88 additions and 11 deletions

View File

@@ -22,6 +22,8 @@ Note that since we don't clearly distinguish between a public and private interf
- Fix cartoon representation not updated when secondary structure changes
- Add Zhang-Skolnick secondary-structure assignment method which handles coarse-grained models (#49)
- Calculate bonds for coarse-grained models
- VolumeServer: Add `health-check` endpoint + `healthCheckPath` config prop to report service health
- ModelServer: Add `health-check` endpoint + `healthCheckPath` config prop to report service health
## [v4.5.0] - 2024-07-28

View File

@@ -1,10 +1,14 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
*/
import * as express from 'express';
import { promises, constants } from 'fs';
import { ConsoleLogger } from '../../mol-util/console-logger';
export function getParam<T>(params: any, ...path: string[]): T | undefined {
@@ -18,4 +22,38 @@ export function getParam<T>(params: any, ...path: string[]): T | undefined {
} catch (e) {
ConsoleLogger.error('Config', `Unable to retrieve property ${path.join('.')} from ${JSON.stringify(params)}`);
}
}
/**
* Used to define a dedicated endpoint to monitor service health. Optionally checks whether source data from file system is readable.
* @param res used to write response
* @param paths array of file paths to check, may be empty
*/
export async function healthCheck(res: express.Response, paths: string[]) {
if (paths.length === 0) {
healthCheckResponse(res, true);
return;
}
for (const path of paths) {
try {
// assert readable file
await promises.access(path, constants.R_OK);
} catch (e) {
ConsoleLogger.error(`Error accessing path ${path}:`, e);
healthCheckResponse(res, false, 'Failed to access data from file system.');
return;
}
}
healthCheckResponse(res, true);
}
function healthCheckResponse(res: express.Response, success: boolean, msg?: string) {
res.writeHead(success ? 200 : 500, {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Requested-With',
});
res.write(success ? msg || 'true' : msg || 'false');
res.end();
}

View File

@@ -1,3 +1,9 @@
# 0.9.12
* add `health-check` endpoint + `healthCheckPath` config prop to report service health
# 0.9.11
# SDF/MOL2 ligand export: fix atom indices when additional atoms are present
# 0.9.10
* /ligand queries: fix atom count reported by SDF/MOL/MOL2 export

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
*/
import * as argparse from 'argparse';
@@ -106,7 +107,12 @@ const DefaultModelServerConfig = {
sourceMap: [
['pdb-cif', 'e:/test/quick/${id}_updated.cif'],
// ['pdb-bcif', 'e:/test/quick/${id}.bcif'],
] as ([string, string] | [string, string, ModelServerFetchFormats])[]
] as ([string, string] | [string, string, ModelServerFetchFormats])[],
/**
* Optionally point to files. The service health-check will assert that all are readable and fail otherwise.
*/
healthCheckPath: [] as string[],
};
export const ModelServerFetchFormats = ['cif', 'bcif', 'cif.gz', 'bcif.gz'] as const;
@@ -198,6 +204,12 @@ function addServerArgs(parser: argparse.ArgumentParser) {
`Supported formats: ${ModelServerFetchFormats.join(', ')}`
].join('\n'),
});
parser.add_argument('--healthCheckPath', {
default: DefaultModelServerConfig.healthCheckPath,
action: 'append',
metavar: 'PATH',
help: `File path(s) to use for health-checks. Will test if all files are accessible and report a failed health-check if that's not the case.`,
});
}
export type ModelServerConfig = typeof DefaultModelServerConfig

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -19,6 +19,7 @@ import { swaggerUiAssetsHandler, swaggerUiIndexHandler } from '../../common/swag
import { MultipleQuerySpec, getMultiQuerySpecFilename } from './api-web-multiple';
import { SimpleResponseResultWriter, WebResutlWriter, TarballResponseResultWriter } from '../utils/writer';
import { splitCamelCase } from '../../../mol-util/string';
import { healthCheck } from '../../common/util';
function makePath(p: string) {
return Config.apiPrefix + '/' + p;
@@ -169,6 +170,9 @@ export function initWebApi(app: express.Express) {
mapQuery(app, q.name, q.definition);
}
// Reports server health depending on `healthCheckPath` config prop
app.get(makePath('health-check'), (_, res) => healthCheck(res, ModelServerConfig.healthCheckPath));
const schema = getApiSchema();
app.get(makePath('openapi.json'), (req, res) => {

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
export const VERSION = '0.9.11';
export const VERSION = '0.9.12';

View File

@@ -1,3 +1,6 @@
# 0.9.6
* Add `health-check` endpoint + `healthCheckPath` config prop to report service health.
# 0.9.5
* Better query response box resolution.

View File

@@ -1,8 +1,9 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
*/
import * as argparse from 'argparse';
@@ -15,7 +16,8 @@ const DefaultServerConfig = {
defaultPort: 1337,
shutdownTimeoutMinutes: 24 * 60, /* a day */
shutdownTimeoutVarianceMinutes: 60,
idMap: [] as [string, string][]
idMap: [] as [string, string][],
healthCheckPath: [] as string[],
};
function addLimitsArgs(parser: argparse.ArgumentParser) {
@@ -81,6 +83,12 @@ function addServerArgs(parser: argparse.ArgumentParser) {
' By default, Mol* Viewer uses `x-ray` and `em`, but any particular use case may vary. '
].join('\n'),
});
parser.add_argument('--healthCheckPath', {
default: DefaultServerConfig.healthCheckPath,
action: 'append',
metavar: 'PATH',
help: `File path(s) to use for health-checks. Will test if all files are accessible and report a failed health-check if that's not the case.`,
});
}
function addJsonConfigArgs(parser: argparse.ArgumentParser) {

View File

@@ -1,2 +1,2 @@
export const VOLUME_SERVER_VERSION = '0.9.5';
export const VOLUME_SERVER_HEADER = `VolumeServer ${VOLUME_SERVER_VERSION}, (c) 2018-2020, Mol* contributors`;
export const VOLUME_SERVER_VERSION = '0.9.6';
export const VOLUME_SERVER_HEADER = `VolumeServer ${VOLUME_SERVER_VERSION}, (c) 2018-2024, Mol* contributors`;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Taken/adapted from DensityServer (https://github.com/dsehnal/DensityServer)
*
@@ -18,6 +18,7 @@ import { LimitsConfig, ServerConfig } from '../config';
import { interpolate } from '../../../mol-util/string';
import { getSchema, shortcutIconLink } from './web-schema';
import { swaggerUiIndexHandler, swaggerUiAssetsHandler } from '../../common/swagger-ui';
import { healthCheck } from '../../common/util';
export function init(app: express.Express) {
app.locals.mapFile = getMapFileFn();
@@ -32,6 +33,9 @@ export function init(app: express.Express) {
// Cell /:src/:id/cell/?text=0|1&space=cartesian|fractional
app.get(makePath(':source/:id/cell/?'), (req, res) => queryBox(req, res, getQueryParams(req, true)));
// Reports server health depending on `healthCheckPath` config prop
app.get(makePath('health-check'), (_, res) => healthCheck(res, ServerConfig.healthCheckPath));
app.get(makePath('openapi.json'), (req, res) => {
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',