From c0f203537c0e345d312eb4879c9dd2c7a9d09ff7 Mon Sep 17 00:00:00 2001 From: Brikaa Date: Fri, 1 Oct 2021 21:41:09 +0200 Subject: [PATCH] config.js: timeout, overrides --- api/src/api/v2.js | 6 ++-- api/src/config.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/api/src/api/v2.js b/api/src/api/v2.js index e3e0522..e0abacd 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -109,8 +109,8 @@ function get_job(body){ stdin: stdin || "", files, timeouts: { - run: run_timeout || 3000, - compile: compile_timeout || 10000, + run: run_timeout || config.run_timeout, + compile: compile_timeout || config.compile_timeout, }, memory_limits: { run: run_memory_limit || config.run_memory_limit, @@ -228,7 +228,7 @@ router.post('/execute', async (req, res) => { return res.status(200).send(result); }catch(error){ - return res.status(400).json(error); + return res.status(400).json(error.to_string()); } }); diff --git a/api/src/config.js b/api/src/config.js index bbd7ae9..162c9d6 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -2,6 +2,46 @@ const fss = require('fs'); const Logger = require('logplease'); const logger = Logger.create('config'); +function parse_overrides(overrides) { + try { + return JSON.parse(overrides); + } + catch (e) { + return null; + } +} + +function validate_overrides(overrides, options) { + for (let language in overrides) { + for (let key in overrides[language]) { + if ( + ![ + 'max_process_count', 'max_open_files', 'max_file_size', + 'compile_memory_limit', 'run_memory_limit', 'compile_timeout', + 'run_timeout', 'output_max_size' + ].includes(key) + ) { + logger.error(`Invalid overridden option: ${key}`); + return false; + } + let option = options.find((o) => o.key == key); + let parser = option.parser; + let raw = overrides[language][key]; + let value = parser(raw); + let validators = option.validators; + for (let validator of validators) { + let response = validator(value, raw); + if (response !== true) { + logger.error(`Failed to validate overridden option: ${key}`, response); + return false; + } + } + overrides[language][key] = value; + } + } + return overrides; +} + const options = [ { key: 'log_level', @@ -91,6 +131,22 @@ const options = [ parser: parse_int, validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], }, + { + key: 'compile_timeout', + desc: + 'Max time allowed for compile stage in milliseconds', + default: 10000, // 10 seconds + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'run_timeout', + desc: + 'Max time allowed for run stage in milliseconds', + default: 3000, // 3 seconds + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, { key: 'compile_memory_limit', desc: @@ -120,6 +176,16 @@ const options = [ default: 64, parser: parse_int, validators: [(x) => x > 0 || `${x} cannot be negative`] + }, + { + key: 'limit_overrides', + desc: 'Per-language exceptions in JSON format for each of:\ + max_process_count, max_open_files, max_file_size, compile_memory_limit,\ + run_memory_limit, compile_timeout, run_timeout, output_max_size', + default: {}, + parser: parse_overrides, + validators: [(x) => !!x || `Invalid JSON format for the overrides\n${x}`] + // More validation is done after the configs are loaded } ]; @@ -138,7 +204,7 @@ options.forEach(option => { const parsed_val = parser(env_val); - const value = env_val || option.default; + const value = parsed_val || option.default; option.validators.for_each(validator => { let response = null; @@ -158,10 +224,16 @@ options.forEach(option => { config[option.key] = value; }); +let overrides = validate_overrides(config.limit_overrides, options) +errored = errored || !overrides; + if (errored) { process.exit(1); } +config.limit_overrides = overrides; +console.log(config.limit_overrides); + logger.info('Configuration successfully loaded'); module.exports = config;