From d2bb4d56729bd4fc02730876260069ce1b4088ba Mon Sep 17 00:00:00 2001 From: Whisperity Date: Mon, 19 Aug 2024 19:11:13 +0200 Subject: [PATCH] feat(server): Asynchronous server-side background task execution This patch implements the whole support ecosystem for server-side background tasks, in order to help lessen the load (and blocking) of API handlers in the web-server for long-running operations. A **Task** is represented by two things in strict co-existence: a lightweight, `pickle`-able implementation in the server's code (a subclass of `AbstractTask`) and a corresponding `BackgroundTask` database entity, which resides in the "configuration" database (shared across all products). A Task is created by API request handlers and then the user is instructed to retain the `TaskToken`: the task's unique identifier. Following, the server will dispatch execution of the object into a background worker process, and keep status synchronisation via the database. Even in a service cluster deployment, load balancing will not interfere with users' ability to query a task's status. While normal users can only query the status of a single task (which is usually automatically done by client code, and not the user manually executing something); product administrators, and especially server administrators have the ability to query an arbitrary set of tasks using the potential filters, with a dedicated API function (`getTasks()`) for this purpose. Tasks can be cancelled only by `SUPERUSER`s, at which point a special binary flag is set in the status record. However, to prevent complicating inter-process communication, cancellation is supposed to be implemented by `AbstractTask` subclasses in a co-operative way. The execution of tasks in a process and a `Task`'s ability to "communicate" with its execution environment is achieved through the new `TaskManager` instance, which is created for every process of a server's deployment. Unfortunately, tasks can die gracelessly if the server is terminated (either internally, or even externally). For this reason, the `DROPPED` status will indicate that the server has terminated prior to, or during a task's execution, and it was unable to produce results. The server was refactored significantly around the handling of subprocesses in order to support various server shutdown scenarios. Servers will start `background_worker_processes` number of task handling subprocesses, which are distinct from the already existing "API handling" subprocesses. By default, if unconfigured, `background_worker_processes` is equal to `worker_processes` (the number of API processes to spawn), which is equal to `$(nproc)` (CPU count in the system). This patch includes a `TestingDummyTask` demonstrative subclass of `AbstractTask` which counts up to an input number of seconds, and each second it gracefully checks whether it is being killed. The corresponding testing API endpoint, `createDummyTask()` can specify whether the task should simulate a failing status. This endpoint can only be used from, but is used extensively, the unit testing of the project. This patch does not include "nice" or "ergonomic" facilities for admins to manage the tasks, and so far, only the server-side of the corresponding API calls are supported. --- .github/workflows/test.yml | 4 +- .../codechecker_analyzer/analysis_manager.py | 39 +- bin/CodeChecker | 49 +- .../compatibility/multiprocessing.py | 15 +- codechecker_common/logger.py | 54 +- codechecker_common/process.py | 49 ++ codechecker_common/util.py | 19 + docs/web/server_config.md | 10 +- docs/web/user_guide.md | 19 +- web/api/Makefile | 9 +- web/api/codechecker_api_shared.thrift | 31 +- .../dist/codechecker-api-6.58.0.tgz | Bin 64458 -> 0 bytes .../dist/codechecker-api-6.59.0.tgz | Bin 0 -> 69586 bytes web/api/js/codechecker-api-node/package.json | 2 +- .../dist/codechecker_api.tar.gz | Bin 60379 -> 66619 bytes web/api/py/codechecker_api/setup.py | 2 +- .../dist/codechecker_api_shared.tar.gz | Bin 7977 -> 7985 bytes web/api/py/codechecker_api_shared/setup.py | 2 +- web/api/report_server.thrift | 82 ++- web/api/tasks.thrift | 160 +++++ web/client/codechecker_client/client.py | 4 +- .../codechecker_client/helpers/results.py | 8 +- web/codechecker_web/shared/version.py | 2 +- .../codechecker_server/api/authentication.py | 4 +- web/server/codechecker_server/api/common.py | 49 ++ .../codechecker_server/api/report_server.py | 49 +- web/server/codechecker_server/api/tasks.py | 393 ++++++++++++ web/server/codechecker_server/cmd/server.py | 88 ++- .../database/config_db_model.py | 197 +++++- ...ented_keeping_track_of_background_tasks.py | 79 +++ web/server/codechecker_server/routing.py | 33 +- web/server/codechecker_server/server.py | 563 +++++++++++++++--- .../codechecker_server/session_manager.py | 37 +- .../task_executors/__init__.py | 7 + .../task_executors/abstract_task.py | 183 ++++++ .../codechecker_server/task_executors/main.py | 142 +++++ .../task_executors/task_manager.py | 229 +++++++ web/server/codechecker_server/tmp.py | 37 -- web/server/config/server_config.json | 2 + web/server/vue-cli/package-lock.json | 12 +- web/server/vue-cli/package.json | 2 +- .../instance_manager/test_instances.py | 3 +- web/tests/functional/tasks/__init__.py | 7 + .../functional/tasks/test_task_management.py | 484 +++++++++++++++ web/tests/libtest/env.py | 38 +- web/tests/libtest/thrift_client_to_db.py | 27 + 46 files changed, 2869 insertions(+), 356 deletions(-) create mode 100644 codechecker_common/process.py delete mode 100644 web/api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz create mode 100644 web/api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz create mode 100644 web/api/tasks.thrift create mode 100644 web/server/codechecker_server/api/common.py create mode 100644 web/server/codechecker_server/api/tasks.py create mode 100644 web/server/codechecker_server/migrations/config/versions/73b04c41885b_implemented_keeping_track_of_background_tasks.py create mode 100644 web/server/codechecker_server/task_executors/__init__.py create mode 100644 web/server/codechecker_server/task_executors/abstract_task.py create mode 100644 web/server/codechecker_server/task_executors/main.py create mode 100644 web/server/codechecker_server/task_executors/task_manager.py delete mode 100644 web/server/codechecker_server/tmp.py create mode 100644 web/tests/functional/tasks/__init__.py create mode 100644 web/tests/functional/tasks/test_task_management.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 00e8ffca07..290d56bb5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,8 +20,8 @@ jobs: - name: Install dependencies run: | pip install $(grep -iE "pylint|pycodestyle" analyzer/requirements_py/dev/requirements.txt) - - name: Run tests - run: make pylint pycodestyle + - name: Run pycodestyle & pylint + run: make -k pycodestyle pylint tools: name: Tools (report-converter, etc.) diff --git a/analyzer/codechecker_analyzer/analysis_manager.py b/analyzer/codechecker_analyzer/analysis_manager.py index 6b22ca4231..6f7e8a22dd 100644 --- a/analyzer/codechecker_analyzer/analysis_manager.py +++ b/analyzer/codechecker_analyzer/analysis_manager.py @@ -13,16 +13,15 @@ import shutil import signal import sys -import time import traceback import zipfile from threading import Timer import multiprocess -import psutil from codechecker_common.logger import get_logger +from codechecker_common.process import kill_process_tree from codechecker_common.review_status_handler import ReviewStatusHandler from codechecker_statistics_collector.collectors.special_return_value import \ @@ -341,42 +340,6 @@ def handle_failure( os.remove(plist_file) -def kill_process_tree(parent_pid, recursive=False): - """Stop the process tree try it gracefully first. - - Try to stop the parent and child processes gracefuly - first if they do not stop in time send a kill signal - to every member of the process tree. - - There is a similar function in the web part please - consider to update that in case of changing this. - """ - proc = psutil.Process(parent_pid) - children = proc.children(recursive) - - # Send a SIGTERM (Ctrl-C) to the main process - proc.terminate() - - # If children processes don't stop gracefully in time, - # slaughter them by force. - _, still_alive = psutil.wait_procs(children, timeout=5) - for p in still_alive: - p.kill() - - # Wait until this process is running. - n = 0 - timeout = 10 - while proc.is_running(): - if n > timeout: - LOG.warning("Waiting for process %s to stop has been timed out" - "(timeout = %s)! Process is still running!", - parent_pid, timeout) - break - - time.sleep(1) - n += 1 - - def setup_process_timeout(proc, timeout, failure_callback=None): """ diff --git a/bin/CodeChecker b/bin/CodeChecker index ad820b8a05..261e2312b2 100755 --- a/bin/CodeChecker +++ b/bin/CodeChecker @@ -6,10 +6,10 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # ------------------------------------------------------------------------- - """ Used to kickstart CodeChecker. -Save original environment without modifications. + +Saves original environment without modifications. Used to run the logging in the same env. """ # This is for enabling CodeChecker as a filename (i.e. module name). @@ -25,9 +25,10 @@ import sys import tempfile PROC_PID = None +EXIT_CODE = None -def run_codechecker(checker_env, subcommand=None): +def run_codechecker(checker_env, subcommand=None) -> int: """ Run the CodeChecker. * checker_env - CodeChecker will be run in the checker env. @@ -63,11 +64,13 @@ def run_codechecker(checker_env, subcommand=None): global PROC_PID PROC_PID = proc.pid - proc.wait() - sys.exit(proc.returncode) + global EXIT_CODE + EXIT_CODE = proc.wait() + + return EXIT_CODE -def main(subcommand=None): +def main(subcommand=None) -> int: original_env = os.environ.copy() checker_env = original_env @@ -94,30 +97,32 @@ def main(subcommand=None): print('Saving original build environment failed.') print(ex) - def signal_term_handler(signum, _frame): + def signal_handler(signum, _frame): + """ + Forwards the received signal to the CodeChecker subprocess started by + this `main` script. + """ global PROC_PID if PROC_PID and sys.platform != "win32": - os.kill(PROC_PID, signal.SIGINT) - - _remove_tmp() - sys.exit(128 + signum) - - signal.signal(signal.SIGTERM, signal_term_handler) - signal.signal(signal.SIGINT, signal_term_handler) - - def signal_reload_handler(_sig, _frame): - global PROC_PID - if PROC_PID: - os.kill(PROC_PID, signal.SIGHUP) + try: + os.kill(PROC_PID, signum) + except ProcessLookupError: + pass + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) if sys.platform != "win32": - signal.signal(signal.SIGHUP, signal_reload_handler) + signal.signal(signal.SIGHUP, signal_handler) + signal.signal(signal.SIGCHLD, signal_handler) try: - run_codechecker(checker_env, subcommand) + global EXIT_CODE + EXIT_CODE = run_codechecker(checker_env, subcommand) finally: _remove_tmp() + return EXIT_CODE + if __name__ == "__main__": - main(None) + sys.exit(main(None) or 0) diff --git a/codechecker_common/compatibility/multiprocessing.py b/codechecker_common/compatibility/multiprocessing.py index 14ef7ebebe..eaee9a78e7 100644 --- a/codechecker_common/compatibility/multiprocessing.py +++ b/codechecker_common/compatibility/multiprocessing.py @@ -13,8 +13,15 @@ # pylint: disable=no-name-in-module # pylint: disable=unused-import if sys.platform in ["darwin", "win32"]: - from multiprocess import Pool # type: ignore - from multiprocess import cpu_count + from multiprocess import \ + Pool, Process, \ + Queue, \ + Value, \ + cpu_count else: - from concurrent.futures import ProcessPoolExecutor as Pool # type: ignore - from multiprocessing import cpu_count + from concurrent.futures import ProcessPoolExecutor as Pool + from multiprocessing import \ + Process, \ + Queue, \ + Value, \ + cpu_count diff --git a/codechecker_common/logger.py b/codechecker_common/logger.py index 8c860dee6e..35702fb0b8 100644 --- a/codechecker_common/logger.py +++ b/codechecker_common/logger.py @@ -6,16 +6,18 @@ # # ------------------------------------------------------------------------- - import argparse +import datetime import json import logging from logging import config from pathlib import Path import os +import sys +from typing import Optional -# The logging leaves can be accesses without -# importing the logging module in other modules. +# The logging leaves can be accesses without importing the logging module in +# other modules. DEBUG = logging.DEBUG INFO = logging.INFO WARNING = logging.WARNING @@ -25,14 +27,24 @@ CMDLINE_LOG_LEVELS = ['info', 'debug_analyzer', 'debug'] -DEBUG_ANALYZER = logging.DEBUG_ANALYZER = 15 # type: ignore +DEBUG_ANALYZER = 15 logging.addLevelName(DEBUG_ANALYZER, 'DEBUG_ANALYZER') +_Levels = {"DEBUG": DEBUG, + "DEBUG_ANALYZER": DEBUG_ANALYZER, + "INFO": INFO, + "WARNING": WARNING, + "ERROR": ERROR, + "CRITICAL": CRITICAL, + "NOTSET": NOTSET, + } + + class CCLogger(logging.Logger): def debug_analyzer(self, msg, *args, **kwargs): - if self.isEnabledFor(logging.DEBUG_ANALYZER): - self._log(logging.DEBUG_ANALYZER, msg, args, **kwargs) + if self.isEnabledFor(DEBUG_ANALYZER): + self._log(DEBUG_ANALYZER, msg, args, **kwargs) logging.setLoggerClass(CCLogger) @@ -113,6 +125,36 @@ def validate_loglvl(log_level): return log_level +def raw_sprint_log(logger: logging.Logger, level: str, message: str) \ + -> Optional[str]: + """ + Formats a raw log `message` using the date format of the specified + `logger`, without actually invoking the logging infrastructure. + """ + if not logger.isEnabledFor(_Levels[level]): + return None + + formatter = logger.handlers[0].formatter if len(logger.handlers) > 0 \ + else None + datefmt = formatter.datefmt if formatter else None + time = datetime.datetime.now().strftime(datefmt) if datefmt \ + else str(datetime.datetime.now()) + + return f"[{validate_loglvl(level)} {time}] - {message}" + + +def signal_log(logger: logging.Logger, level: str, message: str): + """ + Simulates a log output and logs a message within a signal handler, without + triggering a `RuntimeError` due to reentrancy in `print`-like method calls. + """ + formatted = raw_sprint_log(logger, level, message) + if not formatted: + return + + os.write(sys.stderr.fileno(), f"{formatted}\n".encode()) + + class LogCfgServer: """ Initialize a log configuration server for dynamic log configuration. diff --git a/codechecker_common/process.py b/codechecker_common/process.py new file mode 100644 index 0000000000..86da476d2a --- /dev/null +++ b/codechecker_common/process.py @@ -0,0 +1,49 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +import time + +import psutil + +from .logger import get_logger + + +LOG = get_logger("system") + + +def kill_process_tree(parent_pid, recursive=False): + """ + Stop the process tree, gracefully at first. + + Try to stop the parent and child processes gracefuly first. + If they do not stop in time, send a kill signal to every member of the + process tree. + """ + proc = psutil.Process(parent_pid) + children = proc.children(recursive) + + # Send a SIGTERM to the main process. + proc.terminate() + + # If children processes don't stop gracefully in time, slaughter them + # by force. + _, still_alive = psutil.wait_procs(children, timeout=5) + for p in still_alive: + p.kill() + + # Wait until this process is running. + n = 0 + timeout = 10 + while proc.is_running(): + if n > timeout: + LOG.warning("Waiting for process %s to stop has been timed out" + "(timeout = %s)! Process is still running!", + parent_pid, timeout) + break + + time.sleep(1) + n += 1 diff --git a/codechecker_common/util.py b/codechecker_common/util.py index e389b8d1a0..c9075d5599 100644 --- a/codechecker_common/util.py +++ b/codechecker_common/util.py @@ -8,9 +8,12 @@ """ Util module. """ +import datetime +import hashlib import itertools import json import os +import random from typing import TextIO import portalocker @@ -112,3 +115,19 @@ def path_for_fake_root(full_path: str, root_path: str = '/') -> str: def strtobool(value: str) -> bool: """Parse a string value to a boolean.""" return value.lower() in ('y', 'yes', 't', 'true', 'on', '1') + + +def generate_random_token(num_bytes: int = 32) -> str: + """ + Returns a random-generated string usable as a token with `num_bytes` + hexadecimal characters in the output. + """ + prefix = str(os.getpid()).encode() + suffix = str(datetime.datetime.now()).encode() + + hash_value = ''.join( + [hashlib.sha256(prefix + os.urandom(num_bytes * 2) + suffix) + .hexdigest() + for _ in range(0, -(num_bytes // -64))]) + idx = random.randrange(0, len(hash_value) - num_bytes + 1) + return hash_value[idx:(idx + num_bytes)] diff --git a/docs/web/server_config.md b/docs/web/server_config.md index add9bddcb7..7eb0e4f468 100644 --- a/docs/web/server_config.md +++ b/docs/web/server_config.md @@ -17,7 +17,7 @@ Table of Contents * [Size of the compilation database](#size-of-the-compilation-database) * [Authentication](#authentication) -## Number of worker processes +## Number of API worker processes The `worker_processes` section of the config file controls how many processes will be started on the server to process API requests. @@ -25,6 +25,14 @@ will be started on the server to process API requests. The server needs to be restarted if the value is changed in the config file. +### Number of task worker processes +The `background_worker_processes` section of the config file controls how many +processes will be started on the server to process background jobs. + +*Default value*: Fallback to same amount as `worker_processes`. + +The server needs to be restarted if the value is changed in the config file. + ## Run limitation The `max_run_count` section of the config file controls how many runs can be stored on the server for a product. diff --git a/docs/web/user_guide.md b/docs/web/user_guide.md index 846599b76a..bca75ee459 100644 --- a/docs/web/user_guide.md +++ b/docs/web/user_guide.md @@ -145,8 +145,9 @@ or via the `CodeChecker cmd` command-line client. ``` usage: CodeChecker server [-h] [-w WORKSPACE] [-f CONFIG_DIRECTORY] - [--host LISTEN_ADDRESS] [-v PORT] [--not-host-only] - [--skip-db-cleanup] [--config CONFIG_FILE] + [--machine-id MACHINE_ID] [--host LISTEN_ADDRESS] + [-v PORT] [--not-host-only] [--skip-db-cleanup] + [--config CONFIG_FILE] [--sqlite SQLITE_FILE | --postgresql] [--dbaddress DBADDRESS] [--dbport DBPORT] [--dbusername DBUSERNAME] [--dbname DBNAME] @@ -172,6 +173,20 @@ optional arguments: specific configuration (such as authentication settings, and TLS/SSL certificates) from. (default: /home//.codechecker) + --machine-id MACHINE_ID + A unique identifier to be used to identify the machine + running subsequent instances of the "same" server + process. This value is only used internally to + maintain normal function and bookkeeping of executed + tasks following an unclean server shutdown, e.g., + after a crash or system-level interference. If + unspecified, defaults to a reasonable default value + that is generated from the computer's hostname, as + reported by the operating system. In most scenarios, + there is no need to fine-tune this, except if + subsequent executions of the "same" server is achieved + in distinct environments, e.g., if the server + otherwise is running in a container. --host LISTEN_ADDRESS The IP address or hostname of the server on which it should listen for connections. For IPv6 listening, diff --git a/web/api/Makefile b/web/api/Makefile index e145755563..d322ab77b0 100644 --- a/web/api/Makefile +++ b/web/api/Makefile @@ -37,10 +37,11 @@ build: clean target_dirs thrift:$(THRIFT_VERSION) \ /bin/bash -c " \ thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/authentication.thrift && \ - thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/products.thrift && \ - thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/report_server.thrift && \ - thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/configuration.thrift && \ - thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/server_info.thrift" + thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/configuration.thrift && \ + thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/products.thrift && \ + thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/report_server.thrift && \ + thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/server_info.thrift && \ + thrift $(THRIFT_OPTS) $(TARGET_PY) $(TARGET_JS) /data/tasks.thrift" # Create tarball from the API JavaScript part which will be commited in the # repository and installed as a dependency. diff --git a/web/api/codechecker_api_shared.thrift b/web/api/codechecker_api_shared.thrift index 167f8ab40b..3e01ea5fbd 100644 --- a/web/api/codechecker_api_shared.thrift +++ b/web/api/codechecker_api_shared.thrift @@ -4,15 +4,24 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // ------------------------------------------------------------------------- +/** + * Helper enum for expressing a three-way boolean in a filter. + */ +enum Ternary { + BOTH = 0, // Indicates a query where both set and unset booleans are matched. + OFF = 1, // Indicates a query where the filter matches an UNSET boolean. + ON = 2, // Indicates a query where the filter matches a SET boolean. +} + enum ErrorCode { DATABASE, IOERROR, GENERAL, - AUTH_DENIED, // Authentication denied. We do not allow access to the service. - UNAUTHORIZED, // Authorization denied. User does not have right to perform an action. - API_MISMATCH, // The client attempted to query an API version that is not supported by the server. - SOURCE_FILE, // The client sent a source code which contains errors (e.g.: source code comment errors). - REPORT_FORMAT, // The client sent a report with wrong format (e.g. report annotation has bad type in a .plist) + AUTH_DENIED, // Authentication denied. We do not allow access to the service. + UNAUTHORIZED, // Authorization denied. User does not have right to perform an action. + API_MISMATCH, // The client attempted to query an API version that is not supported by the server. + SOURCE_FILE, // The client sent a source code which contains errors (e.g., source code comment errors). + REPORT_FORMAT, // The client sent a report with wrong format (e.g., report annotation has bad type in a .plist). } exception RequestFailed { @@ -30,7 +39,7 @@ exception RequestFailed { * PRODUCT: These permissions are configured per-product. * The extra data field looks like the following object: * { i64 productID } -*/ + */ enum Permission { SUPERUSER = 1, // scope: SYSTEM PERMISSION_VIEW = 2, // scope: SYSTEM @@ -42,8 +51,8 @@ enum Permission { } /** -* Status information about the database backend. -*/ + * Status information about the database backend. + */ enum DBStatus { OK, // Everything is ok with the database. MISSING, // The database is missing. @@ -54,3 +63,9 @@ enum DBStatus { SCHEMA_INIT_ERROR, // Failed to create initial database schema. SCHEMA_UPGRADE_FAILED // Failed to upgrade schema. } + +/** + * Common token type identifying a background task. + * (Main implementation for task management API is in tasks.thrift.) + */ +typedef string TaskToken; diff --git a/web/api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz b/web/api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz deleted file mode 100644 index a8cf8ab10bd14f2623023e3167d40998ccc5f5d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64458 zcmV)%K#jj2iwFP!00002|LnbMbK6LgD7v5hE4YrhYbi#fCh>;d9XHgrJgu{~?6K^b zGuPp8&>|#rOp#iGvRgCT|NYkc0c7DtfC9zka~K&>lv|gBQuwbUt07>7~by*nA$dH9a1r zU&piA;3gT*2P?GJRkFNE2jlrC|2`O}g9#oonGC+%4=$2R+Q|s@zg*m1e;vew#TEL;6t!Di-rXegRd!yBWo}}Z zi`)C<^!n>+u=p`gmIE{;1UOyY55{+^uZ!jMUt~Pi4+OFLI$jOXpx4VW0-RryMrjQuI z{~VIx7HE6u#`BKuR)~C|`JyvG$FxjR-XrSrOEO2Ba5+Ussr9bRQy%$07I%*a2M8Ab zwS0W2o;vz}cu&4hCwI87vfs?OSWXHeZA#N6vDO%c?+$w&+)A{A>j%)?GgoF8FHJD9rrg(I8 zj_KlR^#h| z{P;S({HhQGbXY6I1JKP}Cf}#zC@~hJtzq|Mkf0r2Ecw4s7wnkT+r&Dl3)tn z7qX0-WSPwF(LI=dBOCVxVGG8S`S>O|`0)@VtCV!+{iWR2%3pDhM zlkN2G3!)v2cyPIcU>BJ=nI5|eWFSJvb7BQQWy0N$%A&NMiVc;1ODN%0<%MzkcdY`V6n^Yy?i09rVn1x&t z9?4qJ-!SQ0O6iD;TuC~;o)g-SP!XRI*;o)0F|m4lj`n*zQ19yT zNxTerpf=@A^e%)VJ{bG zzbU)M0a{Fq{4W4@UGnH+tJmW==TXC(G4ud(K|xF63}_kX@x zCg@*NB-wpmTw+R|W9Li-!~=453qhbuc8d`?N9rU!zrFi{V9_=rOue0r5vGe@(3~l+ zP024zvQQN_Rn8a0m`KaH5dnTqhzmO*S2>}#V@!5;;CK!YSaQ3h^8!-D|#}55-?Gj$%TZ_ z@Ri{$pohdq9pB#0u)?{Rqf;lljHwr!+U0CKMf*+LDvLwANIFq>S5VG5y1!{UUQWsV zyjmjq!*!Zu%DJC<^$*fRqzEtO3FC=~03w-0$n^v)3&0mdbzH`8(e$a9r%#Rz@dF

u9u56{WIAxyo}4m9hRWz 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%0D(vvt0cCTcadc@#JkI;iZYXdyr0D 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

W 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 diff --git a/web/api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz b/web/api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1690364ff828fa469ba9bdfabb769a34fb51f337 GIT binary patch literal 69586 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 z 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 z z*wW`V7HPGlv=V4UV3HUlvlb?^RwAiYYZ4&fj$^!~Ty}<2c%Tg1C31#q z=O7jdcBBLqJR(AkASA;MCc{o5Lq&iKv6GWa-0Q4Kf;QJB`$Xn@>Qehgke5xL|i%wzipKG<)1f75~W@^S_PWtu2t}*V~#kByml-tP26kCc*|K* zmInIrj~)#YC0}(~g_-YHR`_*ijzH~iA0aAbU$I)Gn(L}o{4J|pvW;TTSxd^+>aHdm zoA3XY*II}I*xaVAxe%3-?X}4^*QKs-Yjb4l6>4nhacer*0Z(|#zTbs{WVkI`lnjNt z?05uR_OyYB`TXUNK!pjmrfqGFhP|QJHGif2zvAEOhiEokbshHn-)^JT$;SV7Tdn3c z{`V$+bjVidzOwStsc2EKzZ=bi3XLdT`6R;Slk#@8by%(Q&o_QF4g(gLpBDXFMh~A7 zB1DZGI#-V3;CkXa-LqpSiWk=r+j|`mfFoXuNPtk~Bp*frqcsVx`G@=2gFh!)Nk87l z@jQB2tK9|h{o4WhZSz?a#Ix`>MUvhEP@04mt8wXCsv71^5c*?5zw+6?u2JF0 z>LzSF8a>#Ao-IwnTtznb?iqmMItrMoL?$^noRB`5uZZ~oM`Tk zgWJHr`K3Z_zyBqO9amNQ(f{r9EWDxQ-f?nSAy$8;R{I!hdZ zFjcR*&1(I-`&oP&e5vw^cfM|Owy(?n%ki;>I_&np-gFyT`(JN0xAuP{ze}>V60e4d z#h%8*1V?0{=R0?W4f@VI&7!~GD`?;LtB$ktu1oZfRL0kW3Rfg@7_rjoEWE4jJA}_g kMA(UEk~hXEH4WR}_P70Qe@p%T540EVV*mya07vEK>Hq)$ literal 0 HcmV?d00001 diff --git a/web/api/js/codechecker-api-node/package.json b/web/api/js/codechecker-api-node/package.json index 0bfd792add..86e4a596e9 100644 --- a/web/api/js/codechecker-api-node/package.json +++ b/web/api/js/codechecker-api-node/package.json @@ -1,6 +1,6 @@ { "name": "codechecker-api", - "version": "6.58.0", + "version": "6.59.0", "description": "Generated node.js compatible API stubs for CodeChecker server.", "main": "lib", "homepage": "https://github.com/Ericsson/codechecker", diff --git a/web/api/py/codechecker_api/dist/codechecker_api.tar.gz b/web/api/py/codechecker_api/dist/codechecker_api.tar.gz index 3875d3ef7f4af5433227592e105f35a32e274690..ad2f2f4c21dc36af7b17adabaaebf9e7d9294481 100644 GIT binary patch delta 65865 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

    1-(qhld^;%muESa)%C@eGP8Rvy5 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 Drj00x delta 59537 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

    sm3q8%N$lhB|DU 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

      +dA}0Xw<0t%282!4I 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 literal 7977 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 diff --git a/web/api/py/codechecker_api_shared/setup.py b/web/api/py/codechecker_api_shared/setup.py index a4c2e70d02..90f09bf34e 100644 --- a/web/api/py/codechecker_api_shared/setup.py +++ b/web/api/py/codechecker_api_shared/setup.py @@ -8,7 +8,7 @@ with open('README.md', encoding='utf-8', errors="ignore") as f: long_description = f.read() -api_version = '6.58.0' +api_version = '6.59.0' setup( name='codechecker_api_shared', diff --git a/web/api/report_server.thrift b/web/api/report_server.thrift index 359372e28a..962a1c49d8 100644 --- a/web/api/report_server.thrift +++ b/web/api/report_server.thrift @@ -201,6 +201,17 @@ struct RunData { } typedef list RunDataList +struct SubmittedRunOptions { + 1: string runName, + 2: string tag, + 3: string version, // The version of CodeChecker with + // which the analysis was done. + 4: bool force, // If set, existing results in + // the run are removed first. + 5: list trimPathPrefixes, + 6: optional string description, +} + struct RunHistoryData { 1: i64 runId, // Unique id of the run. 2: string runName, // Name of the run. @@ -208,8 +219,7 @@ struct RunHistoryData { 4: string user, // User name who analysed the run. 5: string time, // Date time when the run was analysed. 6: i64 id, // Id of the run history tag. - // !!!DEPRECATED!!! This field will be empty so use the getCheckCommand() API function to get the check command for a run. - 7: string checkCommand, + 7: string checkCommand, // Check command. !!!DEPRECATED!!! This field will be empty so use the getCheckCommand API function to get the check command for a run. 8: string codeCheckerVersion, // CodeChecker client version of the latest analysis. 9: AnalyzerStatisticsData analyzerStatistics, // Statistics for analyzers. Only number of failed and successfully analyzed // files field will be set. To get full analyzer statistics please use the @@ -943,32 +953,47 @@ service codeCheckerDBAccess { //============================================ // The client can ask the server whether a file is already stored in the - // database. If it is, then it is not necessary to send it in the ZIP file - // with massStoreRun() function. This function requires a list of file hashes - // (sha256) and returns the ones which are not stored yet. + // database. + // If it is present, then it is not necessary to send the file in the ZIP + // to the massStoreRunAsynchronous() function. + // This function requires a list of file hashes (sha256) and returns the + // ones which are not stored yet. + // // PERMISSION: PRODUCT_STORE list getMissingContentHashes(1: list fileHashes) throws (1: codechecker_api_shared.RequestFailed requestError), // The client can ask the server whether a blame info is already stored in the - // database. If it is, then it is not necessary to send it in the ZIP file - // with massStoreRun() function. This function requires a list of file hashes - // (sha256) and returns the ones to which no blame info is stored yet. + // database. + // If it is, then it is not necessary to send the info in the ZIP file + // to the massStoreRunAsynchronous() function. + // This function requires a list of file hashes (sha256) and returns the + // ones to which no blame info is stored yet. + // // PERMISSION: PRODUCT_STORE list getMissingContentHashesForBlameInfo(1: list fileHashes) throws (1: codechecker_api_shared.RequestFailed requestError), // This function stores an entire run encapsulated and sent in a ZIP file. - // The ZIP file has to be compressed and sent as a base64 encoded string. The - // ZIP file must contain a "reports" and an optional "root" sub-folder. - // The former one is the output of 'CodeChecker analyze' command and the - // latter one contains the source files on absolute paths starting as if - // "root" was the "/" directory. The source files are not necessary to be - // wrapped in the ZIP file (see getMissingContentHashes() function). + // The ZIP file has to be compressed by ZLib and the compressed buffer + // sent as a Base64-encoded string. The ZIP file must contain a "reports" and + // an optional "root" sub-directory. The former one is the output of the + // 'CodeChecker analyze' command and the latter one contains the source files + // on absolute paths starting as if "root" was the "/" directory. The source + // files are not necessary to be wrapped in the ZIP file + // (see getMissingContentHashes() function). // // The "version" parameter is the used CodeChecker version which checked this // run. // The "force" parameter removes existing analysis results for a run. + // + // !DEPRECATED!: Use of this function is deprecated as the storing client + // process is prone to infinite hangs while waiting for the return value of + // the Thrift call if the network communication terminates during the time + // the server is processing the sent data, which might take a very long time. + // Appropriately modern clients are expected to use the + // massStoreRunAsynchronous() function and the Task API instead! + // // PERMISSION: PRODUCT_STORE i64 massStoreRun(1: string runName, 2: string tag, @@ -979,6 +1004,35 @@ service codeCheckerDBAccess { 7: optional string description) throws (1: codechecker_api_shared.RequestFailed requestError), + // This function stores an entire analysis run encapsulated and sent as a + // ZIP file. The ZIP file must be compressed by ZLib and sent as a + // Base64-encoded string. It must contain a "reports" and an optional "root" + // sub-directory. "reports" contains the output of the `CodeChecker analyze` + // command, while "root", if present, contains the source code of the project + // with their full paths, with the logical "root" replacing the original + // "/" directory. + // + // The source files are not necessary to be present in the ZIP, see + // getMissingContentHashes() for details. + // + // After performing an initial validation of the well-formedness of the + // submitted structure (ill-formedness is reported as an exception), the + // potentially lengthy processing of the data and the database operations are + // done asynchronously. + // + // This function returns a TaskToken, which SHOULD be used as the argument to + // the tasks::getTaskInfo() function such that clients retrieve the + // processing's state. Clients MAY decide to "detach", i.e., not to wait + // for the processing of the submitted data, and ignore the returned handle. + // Even if the client detached, the processing of the stored reports will + // likely eventually conclude. + // + // PERMISSION: PRODUCT_STORE + codechecker_api_shared.TaskToken massStoreRunAsynchronous( + 1: string zipfileBlob, // Base64-encoded string. + 2: SubmittedRunOptions storeOpts) + throws (1: codechecker_api_shared.RequestFailed requestError), + // Returns true if analysis statistics information can be sent to the server, // otherwise it returns false. // PERMISSION: PRODUCT_STORE diff --git a/web/api/tasks.thrift b/web/api/tasks.thrift new file mode 100644 index 0000000000..c92da79434 --- /dev/null +++ b/web/api/tasks.thrift @@ -0,0 +1,160 @@ +// ------------------------------------------------------------------------- +// Part of the CodeChecker project, under the Apache License v2.0 with +// LLVM Exceptions. See LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// ------------------------------------------------------------------------- + +include "codechecker_api_shared.thrift" + +namespace py codeCheckerServersideTasks_v6 +namespace js codeCheckerServersideTasks_v6 + +enum TaskStatus { + ALLOCATED, // Non-terminated state. Token registered but the job hasn't queued yet: the input is still processing. + ENQUEUED, // Non-terminated state. Job in the queue, and all inputs are meaningfully available. + RUNNING, // Non-terminated state. + COMPLETED, // Terminated state. Successfully ran to completion. + FAILED, // Terminated state. Job was running, but the execution failed. + CANCELLED, // Terminated state. Job was cancelled by an administrator, and the cancellation succeeded. + DROPPED, // Terminated state. Job was cancelled due to system reasons (server shutdown, crash, other interference). +} + +struct TaskInfo { + 1: codechecker_api_shared.TaskToken token, + 2: string taskKind, + 3: TaskStatus status, + // If the task is associated with a product, this ID can be used to query + // product information, see products.thirft service. + // The 'productID' is set to 0 if there is no product associated, meaning + // that the task is "global to the server". + 4: i64 productId, + 5: string actorUsername, + 6: string summary, + // Additional, human-readable comments, history, and log output from the + // tasks's processing. + 7: string comments, + 8: i64 enqueuedAtEpoch, + 9: i64 startedAtEpoch, + 10: i64 completedAtEpoch, + 11: i64 lastHeartbeatEpoch, + // Whether the administrator set this job for a co-operative cancellation. + 12: bool cancelFlagSet, +} + +/** + * TaskInfo with additional fields that is sent to administrators only. + */ +struct AdministratorTaskInfo { + 1: TaskInfo normalInfo, + 2: string machineId, // The hopefully unique identifier of the server + // that is/was processing the task. + 3: bool statusConsumed, // Whether the main actor of the task + // (see normalInfo.actorUsername) consumed the + // termination status of the job. +} + +/** + * Metastructure that holds the filters for getTasks(). + * The individual fields of the struct are in "AND" relation with each other. + * For list<> fields, elements of the list filter the same "column" of the + * task information table, and are considered in an "OR" relation. + */ +struct TaskFilter { + 1: list tokens, + 2: list machineIDs, + 3: list kinds, + 4: list statuses, + // If unset, it means "all", including those of no username. + // If an empty list, it means "only no username". + // Otherwise, it means OR the usernames specified. + 5: optional list usernames, + // If unset, it means "all", including those of no product ID. + // If an emtpy list, it means "only those with no associated product". + // Otherwise, it means OR the product IDs specified. + 6: optional list productIDs, + 7: i64 enqueuedBeforeEpoch, + 8: i64 enqueuedAfterEpoch, + 9: i64 startedBeforeEpoch, + 10: i64 startedAfterEpoch, + 11: i64 completedBeforeEpoch, + 12: i64 completedAfterEpoch, + 13: i64 heartbeatBeforeEpoch, + 14: i64 heartbeatAfterEpoch, + 15: codechecker_api_shared.Ternary cancelFlag, + 16: codechecker_api_shared.Ternary consumedFlag, +} + +service codeCheckerServersideTaskService { + // Retrieves the status of a task registered on the server, based on its + // identifying "token". + // + // Following this query, if the task is in any terminating states and the + // query was requested by the main actor, the status will be considered + // "consumed", and might be garbage collected by the server at a later + // point in time. + // + // If the server has authentication enabled, this query is only allowed to + // the following users: + // * The user who originally submitted the request that resulted in the + // creation of this job. + // * If the job is associated with a specific product, anyone with + // PRODUCT_ADMIN privileges for that product. + // * Users with SUPERUSER rights. + // + // PERMISSION: . + TaskInfo getTaskInfo( + 1: codechecker_api_shared.TaskToken token) + throws (1: codechecker_api_shared.RequestFailed requestError), + + // Returns privileged information about the tasks stored in the servers' + // databases, based on the given filter. + // + // This query does not set the "consumed" flag on the results, even if the + // querying user was a task's main actor. + // + // If the querying user only has PRODUCT_ADMIN rights, they are only allowed + // to query the tasks corresponding to a product they are PRODUCT_ADMIN of. + // + // PERMISSION: SUPERUSER, PRODUCT_ADMIN + list getTasks( + 1: TaskFilter filters) + throws (1: codechecker_api_shared.RequestFailed requestError), + + // Sets the specified task's "cancel" flag to TRUE, resulting in a request to + // the task's execution to co-operatively terminate itself. + // Returns whether the current RPC call was the one which set the flag. + // + // Tasks will generally terminate themselves at a safe point during their + // processing, but there are no guarantees that a specific task at any given + // point can reach such a safe point. + // There are no guarantees that a specific task is implemented in a way that + // it can ever be terminated via a "cancel" action. + // + // This method does not result in a communication via operating system + // primitives to the running server, and it is not capable of either + // completely shutting down a running server, or, conversely, to resurrect a + // hung server. + // + // Setting the "cancel" flag of an already cancelled task does nothing, and + // it is not possible to un-cancel a task. + // Setting the "cancel" flag of already terminated tasks does nothing. + // In both such cases, the RPC call will return "bool False". + // + // PERMISSION: SUPERUSER + bool cancelTask( + 1: codechecker_api_shared.TaskToken token) + throws (1: codechecker_api_shared.RequestFailed requestError), + + // Used for testing purposes only. + // This function will **ALWAYS** throw an exception when ran outside of a + // testing environment. + // + // The dummy task will increment a temporary counter in the background, with + // intermittent sleeping, up to approximately "timeout" number of seconds, + // after which point it will gracefully terminate. + // The result of the execution is unsuccessful if "shouldFail" is a true. + codechecker_api_shared.TaskToken createDummyTask( + 1: i32 timeout, + 2: bool shouldFail) + throws (1: codechecker_api_shared.RequestFailed requestError), +} diff --git a/web/client/codechecker_client/client.py b/web/client/codechecker_client/client.py index 730a83446b..0bbe2f69fe 100644 --- a/web/client/codechecker_client/client.py +++ b/web/client/codechecker_client/client.py @@ -205,7 +205,7 @@ def setup_product_client(protocol, host, port, auth_client=None, # Attach to the server-wide product service. product_client = ThriftProductHelper( protocol, host, port, - '/v' + CLIENT_API + '/Products', + f"/v{CLIENT_API}/Products", session_token, lambda: get_new_token(protocol, host, port, cred_manager)) else: @@ -260,6 +260,6 @@ def setup_client(product_url) -> ThriftResultsHelper: return ThriftResultsHelper( protocol, host, port, - '/' + product_name + '/v' + CLIENT_API + '/CodeCheckerService', + f"/{product_name}/v{CLIENT_API}/CodeCheckerService", session_token, lambda: get_new_token(protocol, host, port, cred_manager)) diff --git a/web/client/codechecker_client/helpers/results.py b/web/client/codechecker_client/helpers/results.py index c558cfe040..399623019e 100644 --- a/web/client/codechecker_client/helpers/results.py +++ b/web/client/codechecker_client/helpers/results.py @@ -9,7 +9,7 @@ Helper functions for Thrift api calls. """ -from codechecker_api.codeCheckerDBAccess_v6 import codeCheckerDBAccess +from codechecker_api.codeCheckerDBAccess_v6 import codeCheckerDBAccess, ttypes from codechecker_client.thrift_call import thrift_client_call from .base import BaseClientHelper @@ -181,6 +181,12 @@ def massStoreRun(self, name, tag, version, zipdir, force, trim_path_prefixes, description): pass + @thrift_client_call + def massStoreRunAsynchronous(self, zipfile_blob: str, + store_opts: ttypes.SubmittedRunOptions) \ + -> str: + pass + @thrift_client_call def allowsStoringAnalysisStatistics(self): pass diff --git a/web/codechecker_web/shared/version.py b/web/codechecker_web/shared/version.py index e5d544a750..2ac2d84ae7 100644 --- a/web/codechecker_web/shared/version.py +++ b/web/codechecker_web/shared/version.py @@ -18,7 +18,7 @@ # The newest supported minor version (value) for each supported major version # (key) in this particular build. SUPPORTED_VERSIONS = { - 6: 58 + 6: 59 } # Used by the client to automatically identify the latest major and minor diff --git a/web/server/codechecker_server/api/authentication.py b/web/server/codechecker_server/api/authentication.py index 1430ad9fd6..9e73923e45 100644 --- a/web/server/codechecker_server/api/authentication.py +++ b/web/server/codechecker_server/api/authentication.py @@ -19,6 +19,7 @@ AuthorisationList, HandshakeInformation, Permissions, SessionTokenData from codechecker_common.logger import get_logger +from codechecker_common.util import generate_random_token from codechecker_server.profiler import timeit @@ -28,7 +29,6 @@ from ..permissions import handler_from_scope_params as make_handler, \ require_manager, require_permission from ..server import permissions -from ..session_manager import generate_session_token LOG = get_logger('server') @@ -363,7 +363,7 @@ def newToken(self, description): """ self.__require_privilaged_access() with DBSession(self.__config_db) as session: - token = generate_session_token() + token = generate_random_token(32) user = self.getLoggedInUser() groups = ';'.join(self.__auth_session.groups) session_token = Session(token, user, groups, description, False) diff --git a/web/server/codechecker_server/api/common.py b/web/server/codechecker_server/api/common.py new file mode 100644 index 0000000000..2fc699a24f --- /dev/null +++ b/web/server/codechecker_server/api/common.py @@ -0,0 +1,49 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +import sqlalchemy + +from codechecker_api_shared.ttypes import RequestFailed, ErrorCode + +from codechecker_common.logger import get_logger + + +LOG = get_logger("server") + + +def exc_to_thrift_reqfail(function): + """ + Convert internal exceptions to a `RequestFailed` Thrift exception, which + can be sent back to the RPC client. + """ + func_name = function.__name__ + + def wrapper(*args, **kwargs): + try: + res = function(*args, **kwargs) + return res + except sqlalchemy.exc.SQLAlchemyError as alchemy_ex: + # Convert SQLAlchemy exceptions. + msg = str(alchemy_ex) + import traceback + traceback.print_exc() + + # pylint: disable=raise-missing-from + raise RequestFailed(ErrorCode.DATABASE, msg) + except RequestFailed as rf: + LOG.warning("%s:\n%s", func_name, rf.message) + raise + except Exception as ex: + import traceback + traceback.print_exc() + msg = str(ex) + LOG.warning("%s:\n%s", func_name, msg) + + # pylint: disable=raise-missing-from + raise RequestFailed(ErrorCode.GENERAL, msg) + + return wrapper diff --git a/web/server/codechecker_server/api/report_server.py b/web/server/codechecker_server/api/report_server.py index c98cbc71c0..2348708b81 100644 --- a/web/server/codechecker_server/api/report_server.py +++ b/web/server/codechecker_server/api/report_server.py @@ -44,7 +44,8 @@ ReviewStatusRuleSortType, RunData, RunFilter, RunHistoryData, \ RunReportCount, RunSortType, RunTagCount, \ ReviewStatus as API_ReviewStatus, \ - SourceComponentData, SourceFileData, SortMode, SortType + SourceComponentData, SourceFileData, SortMode, SortType, \ + SubmittedRunOptions from codechecker_common import util from codechecker_common.logger import get_logger @@ -69,6 +70,7 @@ Run, RunHistory, RunHistoryAnalysisInfo, RunLock, \ SourceComponent +from .common import exc_to_thrift_reqfail from .thrift_enum_helper import detection_status_enum, \ detection_status_str, report_status_enum, \ review_status_enum, review_status_str, report_extended_data_type_enum @@ -141,39 +143,6 @@ def slugify(text): return norm_text -def exc_to_thrift_reqfail(function): - """ - Convert internal exceptions to RequestFailed exception - which can be sent back on the thrift connections. - """ - func_name = function.__name__ - - def wrapper(*args, **kwargs): - try: - res = function(*args, **kwargs) - return res - - except sqlalchemy.exc.SQLAlchemyError as alchemy_ex: - # Convert SQLAlchemy exceptions. - msg = str(alchemy_ex) - import traceback - traceback.print_exc() - raise codechecker_api_shared.ttypes.RequestFailed( - codechecker_api_shared.ttypes.ErrorCode.DATABASE, msg) - except codechecker_api_shared.ttypes.RequestFailed as rf: - LOG.warning("%s:\n%s", func_name, rf.message) - raise - except Exception as ex: - import traceback - traceback.print_exc() - msg = str(ex) - LOG.warning("%s:\n%s", func_name, msg) - raise codechecker_api_shared.ttypes.RequestFailed( - codechecker_api_shared.ttypes.ErrorCode.GENERAL, msg) - - return wrapper - - def get_component_values( session: DBSession, component_name: str @@ -3933,6 +3902,18 @@ def massStoreRun(self, name, tag, version, b64zip, force, trim_path_prefixes, description) return m.store() + @exc_to_thrift_reqfail + @timeit + def massStoreRunAsynchronous(self, zipfile_blob: str, + store_opts: SubmittedRunOptions) -> str: + import pprint + LOG.info("massStoreRunAsynchronous() called with:\n\t - %d bytes " + "input\n\t - Options:\n\n%s", len(zipfile_blob), + pprint.pformat(store_opts.__dict__, indent=2, depth=8)) + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.GENERAL, + "massStoreRunAsynchronous() not implemented in this server build!") + @exc_to_thrift_reqfail @timeit def allowsStoringAnalysisStatistics(self): diff --git a/web/server/codechecker_server/api/tasks.py b/web/server/codechecker_server/api/tasks.py new file mode 100644 index 0000000000..3a7686edcc --- /dev/null +++ b/web/server/codechecker_server/api/tasks.py @@ -0,0 +1,393 @@ + +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +""" +Handle Thrift requests for background task management. +""" +import datetime +import os +import time +from typing import Dict, List, Optional + +from sqlalchemy.sql.expression import and_, or_ + +from codechecker_api_shared.ttypes import RequestFailed, ErrorCode, Ternary +from codechecker_api.codeCheckerServersideTasks_v6.ttypes import \ + AdministratorTaskInfo, TaskFilter, TaskInfo, TaskStatus + +from codechecker_common.logger import get_logger + +from codechecker_server.profiler import timeit + +from ..database.config_db_model import BackgroundTask as DBTask, Product +from ..database.database import DBSession, conv +from ..task_executors.abstract_task import AbstractTask, TaskCancelHonoured +from ..task_executors.task_manager import TaskManager +from .. import permissions +from .common import exc_to_thrift_reqfail + +LOG = get_logger("server") + + +class TestingDummyTask(AbstractTask): + """Implementation of task object created by ``createDummyTask()``.""" + def __init__(self, token: str, timeout: int, should_fail: bool): + super().__init__(token, None) + self.timeout = timeout + self.should_fail = should_fail + + def _implementation(self, tm: TaskManager) -> None: + counter: int = 0 + while counter < self.timeout: + tm.heartbeat(self) + + counter += 1 + LOG.debug("Dummy task ticking... [%d / %d]", + counter, self.timeout) + + if tm.should_cancel(self): + LOG.info("Dummy task '%s' was %s at tick [%d / %d]!", + self.token, + "KILLED BY SHUTDOWN" if tm.is_shutting_down + else "CANCELLED BY ADMIN", + counter, + self.timeout) + raise TaskCancelHonoured(self) + + time.sleep(1) + + if self.should_fail: + raise ValueError("Task self-failure as per the user's request.") + + +def _db_timestamp_to_posix_epoch(d: Optional[datetime.datetime]) \ + -> Optional[int]: + return int(d.replace(tzinfo=datetime.timezone.utc).timestamp()) if d \ + else None + + +def _posix_epoch_to_db_timestamp(s: Optional[int]) \ + -> Optional[datetime.datetime]: + return datetime.datetime.fromtimestamp(s, datetime.timezone.utc) if s \ + else None + + +def _make_task_info(t: DBTask) -> TaskInfo: + """Format API `TaskInfo` from `DBTask`.""" + return TaskInfo( + token=t.token, + taskKind=t.kind, + status=TaskStatus._NAMES_TO_VALUES[t.status.upper()], + productId=t.product_id or 0, + actorUsername=t.username, + summary=t.summary, + comments=t.comments, + enqueuedAtEpoch=_db_timestamp_to_posix_epoch(t.enqueued_at), + startedAtEpoch=_db_timestamp_to_posix_epoch(t.started_at), + completedAtEpoch=_db_timestamp_to_posix_epoch(t.finished_at), + lastHeartbeatEpoch=_db_timestamp_to_posix_epoch( + t.last_seen_at), + cancelFlagSet=t.cancel_flag, + ) + + +def _make_admin_task_info(t: DBTask) -> AdministratorTaskInfo: + """Format API `AdministratorTaskInfo` from `DBTask`.""" + return AdministratorTaskInfo( + normalInfo=_make_task_info(t), + machineId=t.machine_id, + statusConsumed=t.consumed, + ) + + +# These names are inherited from Thrift stubs. +# pylint: disable=invalid-name +class ThriftTaskHandler: + """ + Manages Thrift requests concerning the user-facing Background Tasks API. + """ + + def __init__(self, + configuration_database_sessionmaker, + task_manager: TaskManager, + auth_session): + self._config_db = configuration_database_sessionmaker + self._task_manager = task_manager + self._auth_session = auth_session + + def _get_username(self) -> Optional[str]: + """ + Returns the actually logged in user name. + """ + return self._auth_session.user if self._auth_session else None + + @exc_to_thrift_reqfail + @timeit + def getTaskInfo(self, token: str) -> TaskInfo: + """ + Returns the `TaskInfo` for the task identified by `token`. + """ + with DBSession(self._config_db) as session: + db_task: Optional[DBTask] = session.query(DBTask).get(token) + if not db_task: + raise RequestFailed(ErrorCode.GENERAL, + f"Task '{token}' does not exist!") + + has_right_to_query_status: bool = False + should_set_consumed_flag: bool = False + + if db_task.username == self._get_username(): + has_right_to_query_status = True + should_set_consumed_flag = db_task.is_in_terminated_state + elif db_task.product_id is not None: + associated_product: Optional[Product] = \ + session.query(Product).get(db_task.product_id) + if not associated_product: + LOG.error("No product with ID '%d', but a task is " + "associated with it.", + db_task.product_id) + else: + has_right_to_query_status = \ + permissions.require_permission( + permissions.PRODUCT_ADMIN, + {"config_db_session": session, + "productID": associated_product.id}, + self._auth_session) + + if not has_right_to_query_status: + has_right_to_query_status = permissions.require_permission( + permissions.SUPERUSER, + {"config_db_session": session}, + self._auth_session) + + if not has_right_to_query_status: + raise RequestFailed( + ErrorCode.UNAUTHORIZED, + "Only the task's submitter, a PRODUCT_ADMIN (of the " + "product the task is associated with), or a SUPERUSER " + "can getTaskInfo()!") + + info = _make_task_info(db_task) + + if should_set_consumed_flag: + db_task.consumed = True + session.commit() + + return info + + @exc_to_thrift_reqfail + @timeit + def getTasks(self, filters: TaskFilter) -> List[AdministratorTaskInfo]: + """Obtain tasks matching the `filters` for administrators.""" + if filters.filterForNoProductID and filters.productIDs: + raise RequestFailed(ErrorCode.GENERAL, + "Invalid request, do not set " + "\"no product ID\" and some product IDs in " + "the same filter!") + if filters.filterForNoUsername and filters.usernames: + raise RequestFailed(ErrorCode.GENERAL, + "Invalid request, do not set " + "\"no username\" and some usernames in the " + "same filter!") + + with DBSession(self._config_db) as session: + if filters.filterForNoProductID: + if not permissions.require_permission( + permissions.SUPERUSER, + {"config_db_session": session}, + self._auth_session): + raise RequestFailed( + ErrorCode.UNAUTHORIZED, + "Querying service tasks (not associated with a " + "product) requires SUPERUSER privileges!") + if filters.productIDs: + no_admin_products = [ + prod_id for prod_id in filters.productIDs + if not permissions.require_permission( + permissions.PRODUCT_ADMIN, + {"config_db_session": session, "productID": prod_id}, + self._auth_session)] + if no_admin_products: + no_admin_products = [session.query(Product) + .get(product_id).endpoint + for product_id in no_admin_products] + # pylint: disable=consider-using-f-string + raise RequestFailed(ErrorCode.UNAUTHORIZED, + "Querying product tasks requires " + "PRODUCT_ADMIN rights, but it is " + "missing from product(s): '%s'!" + % ("', '".join(no_admin_products))) + + AND = [] + if filters.tokens: + AND.append(or_(*(DBTask.token.ilike(conv(token)) + for token in filters.tokens))) + + if filters.machineIDs: + AND.append(or_(*(DBTask.machine_id.ilike(conv(machine_id)) + for machine_id in filters.machineIDs))) + + if filters.kinds: + AND.append(or_(*(DBTask.kind.ilike(conv(kind)) + for kind in filters.kinds))) + + if filters.statuses: + AND.append(or_(DBTask.status.in_([ + TaskStatus._VALUES_TO_NAMES[status].lower() + for status in filters.statuses]))) + + if filters.usernames is not None: + if filters.usernames: + AND.append(or_(*(DBTask.username.ilike(conv(username)) + for username in filters.usernames))) + else: + AND.append(DBTask.username.is_(None)) + + if filters.productIDs is not None: + if filters.productIDs: + AND.append(or_(DBTask.product_id.in_(filters.productIDs))) + else: + AND.append(DBTask.product_id.is_(None)) + + if filters.enqueuedBeforeEpoch: + AND.append(DBTask.enqueued_at <= _posix_epoch_to_db_timestamp( + filters.enqueuedBeforeEpoch)) + + if filters.enqueuedAfterEpoch: + AND.append(DBTask.enqueued_at >= _posix_epoch_to_db_timestamp( + filters.enqueuedAfterEpoch)) + + if filters.startedBeforeEpoch: + AND.append(DBTask.started_at <= _posix_epoch_to_db_timestamp( + filters.startedBeforeEpoch)) + + if filters.startedAfterEpoch: + AND.append(DBTask.started_at >= _posix_epoch_to_db_timestamp( + filters.startedAfterEpoch)) + + if filters.completedBeforeEpoch: + AND.append(DBTask.finished_at <= _posix_epoch_to_db_timestamp( + filters.completedBeforeEpoch)) + + if filters.completedAfterEpoch: + AND.append(DBTask.finished_at >= _posix_epoch_to_db_timestamp( + filters.completedAfterEpoch)) + + if filters.heartbeatBeforeEpoch: + AND.append(DBTask.last_seen_at <= + _posix_epoch_to_db_timestamp( + filters.heartbeatBeforeEpoch)) + + if filters.heartbeatAfterEpoch: + AND.append(DBTask.last_seen_at >= + _posix_epoch_to_db_timestamp( + filters.heartbeatAfterEpoch)) + + if filters.cancelFlag: + if filters.cancelFlag == Ternary._NAMES_TO_VALUES["OFF"]: + AND.append(DBTask.cancel_flag.is_(False)) + elif filters.cancelFlag == Ternary._NAMES_TO_VALUES["ON"]: + AND.append(DBTask.cancel_flag.is_(True)) + + if filters.consumedFlag: + if filters.consumedFlag == Ternary._NAMES_TO_VALUES["OFF"]: + AND.append(DBTask.consumed.is_(False)) + elif filters.consumedFlag == Ternary._NAMES_TO_VALUES["ON"]: + AND.append(DBTask.consumed.is_(True)) + + ret: List[AdministratorTaskInfo] = [] + has_superuser: Optional[bool] = None + product_admin_rights: Dict[int, bool] = {} + for db_task in session.query(DBTask).filter(and_(*AND)).all(): + if not db_task.product_id: + # Tasks associated with the server, and not a specific + # product, should only be visible to SUPERUSERs. + if has_superuser is None: + has_superuser = permissions.require_permission( + permissions.SUPERUSER, + {"config_db_session": session}, + self._auth_session) + if not has_superuser: + continue + else: + # Tasks associated with a product should only be visible + # to PRODUCT_ADMINs of that product. + try: + if not product_admin_rights[db_task.product_id]: + continue + except KeyError: + product_admin_rights[db_task.product_id] = \ + permissions.require_permission( + permissions.PRODUCT_ADMIN, + {"config_db_session": session, + "productID": db_task.product_id}, + self._auth_session) + if not product_admin_rights[db_task.product_id]: + continue + + ret.append(_make_admin_task_info(db_task)) + + return ret + + @exc_to_thrift_reqfail + @timeit + def cancelTask(self, token: str) -> bool: + """ + Sets the ``cancel_flag`` of the task specified by `token` to `True` + in the database, **REQUESTING** that the task gracefully terminate + itself. + + There are no guarantees that tasks will respect this! + """ + with DBSession(self._config_db) as session: + if not permissions.require_permission( + permissions.SUPERUSER, + {"config_db_session": session}, + self._auth_session): + raise RequestFailed( + ErrorCode.UNAUTHORIZED, + "cancelTask() requires server-level SUPERUSER rights.") + + db_task: Optional[DBTask] = session.query(DBTask).get(token) + if not db_task: + raise RequestFailed(ErrorCode.GENERAL, + f"Task '{token}' does not exist!") + + if not db_task.can_be_cancelled: + return False + + db_task.add_comment("SUPERUSER requested cancellation.", + self._get_username()) + db_task.cancel_flag = True + session.commit() + + return True + + @exc_to_thrift_reqfail + @timeit + def createDummyTask(self, timeout: int, should_fail: bool) -> str: + """ + Used for testing purposes only. + + This function will **ALWAYS** throw an exception when ran outside of a + testing environment. + """ + if "TEST_WORKSPACE" not in os.environ: + raise RequestFailed(ErrorCode.GENERAL, + "createDummyTask() is only available in " + "testing environments!") + + token = self._task_manager.allocate_task_record( + "TaskService::DummyTask", + "Dummy task for testing purposes", + self._get_username(), + None) + + t = TestingDummyTask(token, timeout, should_fail) + self._task_manager.push_task(t) + + return token diff --git a/web/server/codechecker_server/cmd/server.py b/web/server/codechecker_server/cmd/server.py index 33bbbd20f1..7b49982669 100644 --- a/web/server/codechecker_server/cmd/server.py +++ b/web/server/codechecker_server/cmd/server.py @@ -18,13 +18,11 @@ import signal import socket import sys -import time from typing import List, Optional, Tuple, cast from alembic import config from alembic import script from alembic.util import CommandError -import psutil from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import sessionmaker @@ -32,7 +30,7 @@ from codechecker_report_converter import twodim -from codechecker_common import arg, cmd_config, logger, util +from codechecker_common import arg, cmd_config, logger, process, util from codechecker_common.compatibility.multiprocessing import Pool, cpu_count from codechecker_server import instance_manager, server @@ -101,6 +99,25 @@ def add_arguments_to_parser(parser): "authentication settings, TLS certificate" " (cert.pem) and key (key.pem)) from.") + parser.add_argument("--machine-id", + type=str, + dest="machine_id", + default=argparse.SUPPRESS, + required=False, + help=""" +A unique identifier to be used to identify the machine running subsequent +instances of the "same" server process. +This value is only used internally to maintain normal function and bookkeeping +of executed tasks following an unclean server shutdown, e.g., after a crash or +system-level interference. + +If unspecified, defaults to a reasonable default value that is generated from +the computer's hostname, as reported by the operating system. +In most scenarios, there is no need to fine-tune this, except if subsequent +executions of the "same" server is achieved in distinct environments, e.g., +if the server otherwise is running in a container. +""") + parser.add_argument('--host', type=str, dest="listen_address", @@ -424,7 +441,7 @@ def arg_match(options): setattr(args, "instance_manager", True) # If everything is fine, do call the handler for the subcommand. - main(args) + return main(args) parser.set_defaults( func=__handle, func_process_config_file=cmd_config.process_config_file) @@ -762,42 +779,6 @@ def _get_migration_decisions() -> List[Tuple[str, str, bool]]: return 0 -def kill_process_tree(parent_pid, recursive=False): - """Stop the process tree try it gracefully first. - - Try to stop the parent and child processes gracefuly - first if they do not stop in time send a kill signal - to every member of the process tree. - - There is a similar function in the analyzer part please - consider to update that in case of changing this. - """ - proc = psutil.Process(parent_pid) - children = proc.children(recursive) - - # Send a SIGTERM (Ctrl-C) to the main process - proc.terminate() - - # If children processes don't stop gracefully in time, - # slaughter them by force. - _, still_alive = psutil.wait_procs(children, timeout=5) - for p in still_alive: - p.kill() - - # Wait until this process is running. - n = 0 - timeout = 10 - while proc.is_running(): - if n > timeout: - LOG.warning("Waiting for process %s to stop has been timed out" - "(timeout = %s)! Process is still running!", - parent_pid, timeout) - break - - time.sleep(1) - n += 1 - - def __instance_management(args): """Handles the instance-manager commands --list/--stop/--stop-all.""" @@ -842,7 +823,7 @@ def __instance_management(args): continue try: - kill_process_tree(i['pid']) + process.kill_process_tree(i['pid']) LOG.info("Stopped CodeChecker server running on port %s " "in workspace %s (PID: %s)", i['port'], i['workspace'], i['pid']) @@ -1106,16 +1087,21 @@ def server_init_start(args): 'doc_root': context.doc_root, 'version': context.package_git_tag} + # Create a machine ID if the user did not specify one. + machine_id = getattr(args, "machine_id", + f"{socket.gethostname()}:{args.view_port}") + try: - server.start_server(args.config_directory, - package_data, - args.view_port, - cfg_sql_server, - args.listen_address, - 'force_auth' in args, - args.skip_db_cleanup, - context, - environ) + return server.start_server(args.config_directory, + package_data, + args.view_port, + cfg_sql_server, + args.listen_address, + 'force_auth' in args, + args.skip_db_cleanup, + context, + environ, + machine_id) except socket.error as err: if err.errno == errno.EADDRINUSE: LOG.error("Server can't be started, maybe port number (%s) is " @@ -1152,4 +1138,4 @@ def main(args): except FileNotFoundError as fnerr: LOG.error(fnerr) sys.exit(1) - server_init_start(args) + return server_init_start(args) diff --git a/web/server/codechecker_server/database/config_db_model.py b/web/server/codechecker_server/database/config_db_model.py index 00f0c4948e..e2ee5a550b 100644 --- a/web/server/codechecker_server/database/config_db_model.py +++ b/web/server/codechecker_server/database/config_db_model.py @@ -8,8 +8,9 @@ """ SQLAlchemy ORM model for the product configuration database. """ -from datetime import datetime +from datetime import datetime, timezone import sys +from typing import Optional from sqlalchemy import Boolean, CHAR, Column, DateTime, Enum, ForeignKey, \ Integer, MetaData, String, Text @@ -158,6 +159,200 @@ def __init__(self, config_key, config_value): self.config_value = config_value +class BackgroundTask(Base): + """ + Information about background tasks executed on a CodeChecker service, + potentially as part of a cluster, stored in the database. + These entities store the metadata for the task objects, but no information + about the actual "input" of the task exists in the database! + """ + __tablename__ = "background_tasks" + + _token_length = 64 + + machine_id = Column(String, index=True) + """ + A unique, implementation-specific identifier of the actual CodeChecker + server instance that knows how to execute the task. + """ + + token = Column(CHAR(length=_token_length), primary_key=True) + kind = Column(String, nullable=False, index=True) + status = Column(Enum( + # A job token (and thus a BackgroundTask record) was allocated, but + # the job is still under preparation. + "allocated", + + # The job is pending on the server, but the server has all the data + # available to eventually perform the job. + "enqueued", + + # The server is actually performing the job. + "running", + + # The server successfully finished completing the job. + "completed", + + # The execution of the job failed. + # In this stage, the "comments" field likely contains more information + # that is not machine-readable. + "failed", + + # The job never started, or its execution was terminated at the + # request of the administrators. + "cancelled", + + # The job never started, or its execution was terminated due to a + # system-level reason (such as the server's foced shutdown). + "dropped", + ), + nullable=False, + default="enqueued", + index=True) + + product_id = Column(Integer, + ForeignKey("products.id", + deferrable=False, + initially="IMMEDIATE", + ondelete="CASCADE"), + nullable=True, + index=True) + """ + If the job is tightly associated with a product, the ID of the `Product` + entity with which it is associated. + """ + + username = Column(String, nullable=True) + """ + The main actor who was responsible for the creation of the job task. + """ + + summary = Column(String, nullable=False) + comments = Column(Text, nullable=True) + + enqueued_at = Column(DateTime, nullable=True) + started_at = Column(DateTime, nullable=True) + finished_at = Column(DateTime, nullable=True) + + last_seen_at = Column(DateTime, nullable=True) + """ + Contains the timestamp, only when the job is not yet "finished", when the + job last synchronised against the database, e.g., when it last checked the + "cancel_flag" field. + + This is used for health checking whether the background worker is actually + doing something, as a second line of defence to uncover "dropped" jobs, + e.g., when the servers have failed and the new server can not identify + jobs from its "previous life". + """ + + consumed = Column(Boolean, nullable=False, + default=False, server_default=false()) + """ + Whether the status of the job was checked **BY THE MAIN ACTOR** (username). + """ + + cancel_flag = Column(Boolean, nullable=False, + default=False, server_default=false()) + """ + Whether a SUPERUSER has signalled that the job should be cancelled. + + Note, that cancelling is a co-operative action: jobs are never actually + "killed" on the O.S. level from the outside; rather, each job is expected + to be implemented in a way that they regularly query this bit, and if set, + act accordingly. + """ + + def __init__(self, + token: str, + kind: str, + summary: str, + machine_id: str, + user_name: Optional[str], + product: Optional[Product] = None, + ): + self.machine_id = machine_id + self.token = token + self.kind = kind + self.status = "allocated" + self.summary = summary + self.username = user_name + self.last_seen_at = datetime.now(timezone.utc) + + if product: + self.product_id = product.id + + def add_comment(self, comment: str, actor: Optional[str] = None): + if not self.comments: + self.comments = "" + elif self.comments: + self.comments += "\n----------\n" + + self.comments += f"{actor if actor else ''} " \ + f"at {str(datetime.now(timezone.utc))}:\n{comment}" + + def heartbeat(self): + """Update `last_seen_at`.""" + if self.status in ["enqueued", "running"]: + self.last_seen_at = datetime.now(timezone.utc) + + def set_enqueued(self): + """Marks the job as successfully enqueued.""" + if self.status != "allocated": + raise ValueError( + f"Invalid transition '{str(self.status)}' -> 'enqueued'") + + self.status = "enqueued" + self.enqueued_at = datetime.now(timezone.utc) + + def set_running(self): + """Marks the job as currently executing.""" + if self.status != "enqueued": + raise ValueError( + f"Invalid transition '{str(self.status)}' -> 'running'") + + self.status = "running" + self.started_at = datetime.now(timezone.utc) + + def set_finished(self, successfully: bool = True): + """Marks the job as successfully completed or failed.""" + new_status = "completed" if successfully else "failed" + if self.status != "running": + raise ValueError( + f"Invalid transition '{str(self.status)}' -> '{new_status}'") + + self.status = new_status + self.finished_at = datetime.now(timezone.utc) + + def set_abandoned(self, force_dropped_status: bool = False): + """ + Marks the job as cancelled or dropped based on whether the + cancel flag is set. + """ + new_status = "cancelled" \ + if not force_dropped_status and self.cancel_flag \ + else "dropped" + + self.status = new_status + self.finished_at = datetime.now(timezone.utc) + + @property + def is_in_terminated_state(self) -> bool: + """ + Returns whether the current task has finished execution in some way, + for some reason. + """ + return self.status not in ["allocated", "enqueued", "running"] + + @property + def can_be_cancelled(self) -> bool: + """ + Returns whether the task is in a state where setting `cancel_flag` + is meaningful. + """ + return not self.is_in_terminated_state and not self.cancel_flag + + IDENTIFIER = { 'identifier': "ConfigDatabase", 'orm_meta': CC_META diff --git a/web/server/codechecker_server/migrations/config/versions/73b04c41885b_implemented_keeping_track_of_background_tasks.py b/web/server/codechecker_server/migrations/config/versions/73b04c41885b_implemented_keeping_track_of_background_tasks.py new file mode 100644 index 0000000000..45b3ab7bc1 --- /dev/null +++ b/web/server/codechecker_server/migrations/config/versions/73b04c41885b_implemented_keeping_track_of_background_tasks.py @@ -0,0 +1,79 @@ +""" +Implemented keeping track of background tasks through corresponding records +in the server-wide configuration database. + +Revision ID: 73b04c41885b +Revises: 00099e8bc212 +Create Date: 2023-09-21 14:24:27.395597 +""" + +from alembic import op +import sqlalchemy as sa + + +# Revision identifiers, used by Alembic. +revision = '73b04c41885b' +down_revision = '00099e8bc212' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "background_tasks", + sa.Column("machine_id", sa.String(), nullable=True), + sa.Column("token", sa.CHAR(length=64), nullable=False), + sa.Column("kind", sa.String(), nullable=False), + sa.Column("status", sa.Enum("allocated", + "enqueued", + "running", + "completed", + "failed", + "cancelled", + "dropped", + name="background_task_statuses"), + nullable=False), + sa.Column("product_id", sa.Integer(), nullable=True), + sa.Column("summary", sa.String(), nullable=False), + sa.Column("comments", sa.Text(), nullable=True), + sa.Column("username", sa.String(), nullable=True), + sa.Column("enqueued_at", sa.DateTime(), nullable=True), + sa.Column("started_at", sa.DateTime(), nullable=True), + sa.Column("finished_at", sa.DateTime(), nullable=True), + sa.Column("last_seen_at", sa.DateTime(), nullable=True), + sa.Column("consumed", sa.Boolean(), nullable=False, + server_default=sa.false()), + sa.Column("cancel_flag", sa.Boolean(), nullable=False, + server_default=sa.false()), + + sa.ForeignKeyConstraint( + ["product_id"], ["products.id"], + name=op.f("fk_background_tasks_product_id_products"), + deferrable=False, + ondelete="CASCADE", + initially="IMMEDIATE"), + sa.PrimaryKeyConstraint("token", name=op.f("pk_background_tasks")) + ) + op.create_index(op.f("ix_background_tasks_kind"), "background_tasks", + ["kind"], unique=False) + op.create_index(op.f("ix_background_tasks_machine_id"), "background_tasks", + ["machine_id"], unique=False) + op.create_index(op.f("ix_background_tasks_product_id"), "background_tasks", + ["product_id"], unique=False) + op.create_index(op.f("ix_background_tasks_status"), "background_tasks", + ["status"], unique=False) + + +def downgrade(): + ctx = op.get_context() + dialect = ctx.dialect.name + + op.drop_index(op.f("ix_background_tasks_status"), "background_tasks") + op.drop_index(op.f("ix_background_tasks_product_id"), "background_tasks") + op.drop_index(op.f("ix_background_tasks_machine_id"), "background_tasks") + op.drop_index(op.f("ix_background_tasks_kind"), "background_tasks") + + op.drop_table("action_history") + + if dialect == "postgresql": + op.execute("DROP TYPE background_task_statuses;") diff --git a/web/server/codechecker_server/routing.py b/web/server/codechecker_server/routing.py index 79ac8d0686..34fbb82f87 100644 --- a/web/server/codechecker_server/routing.py +++ b/web/server/codechecker_server/routing.py @@ -15,25 +15,28 @@ from codechecker_web.shared.version import SUPPORTED_VERSIONS -# A list of top-level path elements under the webserver root -# which should not be considered as a product route. -NON_PRODUCT_ENDPOINTS = ['index.html', - 'images', - 'docs', - 'live', - 'ready'] +# A list of top-level path elements under the webserver root which should not +# be considered as a product route. +NON_PRODUCT_ENDPOINTS = ["index.html", + "images", + "docs", + "live", + "ready", + ] # A list of top-level path elements in requests (such as Thrift endpoints) # which should not be considered as a product route. -NON_PRODUCT_ENDPOINTS += ['Authentication', - 'Products', - 'CodeCheckerService'] +NON_PRODUCT_ENDPOINTS += ["Authentication", + "Products", + "CodeCheckerService", + "Tasks", + ] # A list of top-level path elements under the webserver root which should -# be protected by authentication requirement when accessing the server. +# be protected by authentication requirements when accessing the server. PROTECTED_ENTRY_POINTS = ['', # Empty string in a request is 'index.html'. - 'index.html'] + "index.html"] def is_valid_product_endpoint(uripart): @@ -68,9 +71,8 @@ def is_supported_version(version): If supported, returns the major and minor version as a tuple. """ - version = version.lstrip('v') - version_parts = version.split('.') + version_parts = version.split('.', 2) # We don't care if accidentally the version tag contains a revision number. major, minor = int(version_parts[0]), int(version_parts[1]) @@ -113,9 +115,8 @@ def split_client_POST_request(path): Returns the product endpoint, the API version and the API service endpoint as a tuple of 3. """ - # A standard POST request from an API client looks like: - # http://localhost:8001/[product-name]// + # http://localhost:8001/[product-name]/v/ # where specifying the product name is optional. split_path = urlparse(path).path.split('/', 3) diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index 40bdf6db4d..f10f28291e 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -12,6 +12,7 @@ import atexit +from collections import Counter import datetime from functools import partial from hashlib import sha256 @@ -25,10 +26,10 @@ import ssl import sys import stat -from typing import List, Optional, Tuple +import time +from typing import Dict, List, Optional, Tuple, cast import urllib -import multiprocess from sqlalchemy.orm import sessionmaker from sqlalchemy.sql.expression import func from thrift.protocol import TJSONProtocol @@ -47,11 +48,14 @@ codeCheckerProductService as ProductAPI_v6 from codechecker_api.ServerInfo_v6 import \ serverInfoService as ServerInfoAPI_v6 +from codechecker_api.codeCheckerServersideTasks_v6 import \ + codeCheckerServersideTaskService as TaskAPI_v6 from codechecker_common import util -from codechecker_common.logger import get_logger from codechecker_common.compatibility.multiprocessing import \ - Pool, cpu_count + Pool, Process, Queue, Value, cpu_count +from codechecker_common.logger import get_logger, signal_log +from codechecker_common.util import generate_random_token from codechecker_web.shared import database_status from codechecker_web.shared.version import get_version_str @@ -63,12 +67,15 @@ from .api.report_server import ThriftRequestHandler as ReportHandler_v6 from .api.server_info_handler import \ ThriftServerInfoHandler as ServerInfoHandler_v6 +from .api.tasks import ThriftTaskHandler as TaskHandler_v6 from .database import database, db_cleanup from .database.config_db_model import Product as ORMProduct, \ Configuration as ORMConfiguration from .database.database import DBSession from .database.run_db_model import IDENTIFIER as RUN_META, Run, RunLock -from .tmp import get_tmp_dir_hash +from .task_executors.main import executor as background_task_executor +from .task_executors.task_manager import \ + TaskManager as BackgroundTaskManager, drop_all_incomplete_tasks LOG = get_logger('server') @@ -85,8 +92,8 @@ def __init__(self, request, client_address, server): self.path = None super().__init__(request, client_address, server) - def log_message(self, *args): - """ Silencing http server. """ + def log_message(self, *_args): + """Silencing HTTP server.""" return def send_thrift_exception(self, error_msg, iprot, oprot, otrans): @@ -104,7 +111,7 @@ def send_thrift_exception(self, error_msg, iprot, oprot, otrans): result = otrans.getvalue() self.send_response(200) self.send_header("content-type", "application/x-thrift") - self.send_header("Content-Length", len(result)) + self.send_header("Content-Length", str(len(result))) self.end_headers() self.wfile.write(result) @@ -369,22 +376,22 @@ def do_POST(self): major_version, _ = version_supported if major_version == 6: - if request_endpoint == 'Authentication': + if request_endpoint == "Authentication": auth_handler = AuthHandler_v6( self.server.manager, self.auth_session, self.server.config_session) processor = AuthAPI_v6.Processor(auth_handler) - elif request_endpoint == 'Configuration': + elif request_endpoint == "Configuration": conf_handler = ConfigHandler_v6( self.auth_session, self.server.config_session) processor = ConfigAPI_v6.Processor(conf_handler) - elif request_endpoint == 'ServerInfo': + elif request_endpoint == "ServerInfo": server_info_handler = ServerInfoHandler_v6(version) processor = ServerInfoAPI_v6.Processor( server_info_handler) - elif request_endpoint == 'Products': + elif request_endpoint == "Products": prod_handler = ProductHandler_v6( self.server, self.auth_session, @@ -392,7 +399,13 @@ def do_POST(self): product, version) processor = ProductAPI_v6.Processor(prod_handler) - elif request_endpoint == 'CodeCheckerService': + elif request_endpoint == "Tasks": + task_handler = TaskHandler_v6( + self.server.config_session, + self.server.task_manager, + self.auth_session) + processor = TaskAPI_v6.Processor(task_handler) + elif request_endpoint == "CodeCheckerService": # This endpoint is a product's report_server. if not product: error_msg = \ @@ -745,7 +758,10 @@ def __init__(self, pckg_data, context, check_env, - manager): + manager: session_manager.SessionManager, + machine_id: str, + task_queue: Queue, + server_shutdown_flag: Value): LOG.debug("Initializing HTTP server...") @@ -756,6 +772,7 @@ def __init__(self, self.context = context self.check_env = check_env self.manager = manager + self.address, self.port = server_address self.__products = {} # Create a database engine for the configuration database. @@ -764,6 +781,12 @@ def __init__(self, self.config_session = sessionmaker(bind=self.__engine) self.manager.set_database_connection(self.config_session) + self.__task_queue = task_queue + self.task_manager = BackgroundTaskManager(task_queue, + self.config_session, + server_shutdown_flag, + machine_id) + # Load the initial list of products and set up the server. cfg_sess = self.config_session() permissions.initialise_defaults('SYSTEM', { @@ -780,7 +803,7 @@ def __init__(self, cfg_sess.close() try: - HTTPServer.__init__(self, server_address, + HTTPServer.__init__(self, (self.address, self.port), RequestHandlerClass, bind_and_activate=True) ssl_key_file = os.path.join(config_directory, "key.pem") @@ -806,13 +829,23 @@ def __init__(self, else: LOG.info("Searching for SSL key at %s, cert at %s, " - "not found...", ssl_key_file, ssl_cert_file) + "not found!", ssl_key_file, ssl_cert_file) LOG.info("Falling back to simple, insecure HTTP.") except Exception as e: LOG.error("Couldn't start the server: %s", e.__str__()) raise + # If the server was started with the port 0, the OS will pick an + # available port. + # For this reason, we will update the port variable after server + # ininitialisation. + self.port = self.socket.getsockname()[1] + + @property + def formatted_address(self) -> str: + return f"{str(self.address)}:{self.port}" + def configure_keepalive(self): """ Enable keepalive on the socket and some TCP keepalive configuration @@ -855,17 +888,40 @@ def configure_keepalive(self): LOG.error('Failed to set TCP max keepalive probe: %s', ret) def terminate(self): - """ - Terminating the server. - """ + """Terminates the server and releases associated resources.""" try: self.server_close() + self.__task_queue.close() + self.__task_queue.join_thread() self.__engine.dispose() + + sys.exit(128 + signal.SIGINT) except Exception as ex: LOG.error("Failed to shut down the WEB server!") LOG.error(str(ex)) sys.exit(1) + def serve_forever_with_shutdown_handler(self): + """ + Calls `HTTPServer.serve_forever` but handles SIGINT (2) signals + gracefully such that the open resources are properly cleaned up. + """ + def _handler(signum: int, _frame): + if signum not in [signal.SIGINT]: + signal_log(LOG, "ERROR", "Signal " + f"<{signal.Signals(signum).name} ({signum})> " + "handling attempted by " + "'serve_forever_with_shutdown_handler'!") + return + + signal_log(LOG, "DEBUG", f"{os.getpid()}: Received " + f"{signal.Signals(signum).name} ({signum}), " + "performing shutdown ...") + self.terminate() + + signal.signal(signal.SIGINT, _handler) + return self.serve_forever() + def add_product(self, orm_product, init_db=False): """ Adds a product to the list of product databases connected to @@ -990,6 +1046,10 @@ class CCSimpleHttpServerIPv6(CCSimpleHttpServer): address_family = socket.AF_INET6 + @property + def formatted_address(self) -> str: + return f"[{str(self.address)}]:{self.port}" + def __make_root_file(root_file): """ @@ -1000,7 +1060,7 @@ def __make_root_file(root_file): LOG.debug("Generating initial superuser (root) credentials...") username = ''.join(sample("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 6)) - password = get_tmp_dir_hash()[:8] + password = generate_random_token(8) LOG.info("A NEW superuser credential was generated for the server. " "This information IS SAVED, thus subsequent server starts " @@ -1028,16 +1088,16 @@ def __make_root_file(root_file): return secret -def start_server(config_directory, package_data, port, config_sql_server, - listen_address, force_auth, skip_db_cleanup: bool, - context, check_env): +def start_server(config_directory: str, package_data, port: int, + config_sql_server, listen_address: str, + force_auth: bool, skip_db_cleanup: bool, + context, check_env, machine_id: str) -> int: """ - Start http server to handle web client and thrift requests. + Starts the HTTP server to handle Web client and Thrift requests, execute + background jobs. """ LOG.debug("Starting CodeChecker server...") - server_addr = (listen_address, port) - root_file = os.path.join(config_directory, 'root.user') if not os.path.exists(root_file): LOG.warning("Server started without 'root.user' present in " @@ -1103,92 +1163,445 @@ def start_server(config_directory, package_data, port, config_sql_server, else: LOG.debug("Skipping db_cleanup, as requested.") + def _cleanup_incomplete_tasks(action: str) -> int: + config_session_factory = config_sql_server.create_engine() + try: + return drop_all_incomplete_tasks( + sessionmaker(bind=config_session_factory), + machine_id, action) + finally: + config_session_factory.dispose() + + dropped_tasks = _cleanup_incomplete_tasks( + "New server started with the same machine_id, assuming the old " + "server is dead and won't be able to finish the task.") + if dropped_tasks: + LOG.info("At server startup, dropped %d background tasks left behind " + "by a previous server instance matching machine ID '%s'.", + dropped_tasks, machine_id) + + api_processes: Dict[int, Process] = {} + requested_api_threads = cast(int, manager.worker_processes) \ + or cpu_count() + + bg_processes: Dict[int, Process] = {} + requested_bg_threads = cast(int, + manager.background_worker_processes) \ + or requested_api_threads + # Note that Queue under the hood uses OS-level primitives such as a socket + # or a pipe, where the read-write buffers have a **LIMITED** capacity, and + # are usually **NOT** backed by the full amount of available system memory. + bg_task_queue: Queue = Queue() + is_server_shutting_down = Value('B', False) + server_clazz = CCSimpleHttpServer - if ':' in server_addr[0]: + if ':' in listen_address: # IPv6 address specified for listening. # FIXME: Python>=3.8 automatically handles IPv6 if ':' is in the bind # address, see https://bugs.python.org/issue24209. server_clazz = CCSimpleHttpServerIPv6 - http_server = server_clazz(server_addr, + http_server = server_clazz((listen_address, port), RequestHandler, config_directory, config_sql_server, package_data, context, check_env, - manager) + manager, + machine_id, + bg_task_queue, + is_server_shutting_down) + + try: + instance_manager.register(os.getpid(), + os.path.abspath( + context.codechecker_workspace), + port) + except IOError as ex: + LOG.debug(ex.strerror) - # If the server was started with the port 0, the OS will pick an available - # port. For this reason we will update the port variable after server - # initialization. - port = http_server.socket.getsockname()[1] + def unregister_handler(pid): + # Handle errors during instance unregistration. + # The workspace might be removed so updating the config content might + # fail. + try: + instance_manager.unregister(pid) + except IOError as ex: + LOG.debug(ex.strerror) - processes = [] + atexit.register(unregister_handler, os.getpid()) - def signal_handler(signum, _): + def _start_process_with_no_signal_handling(**kwargs): """ - Handle SIGTERM to stop the server running. + Starts a `multiprocessing.Process` in a context where the signal + handling is temporarily disabled, such that the child process does not + inherit any signal handling from the parent. + + Child processes spawned after the main process set up its signals + MUST NOT inherit the signal handling because that would result in + multiple children firing on the SIGTERM handler, for example. + + For this reason, we temporarily disable the signal handling here by + returning to the initial defaults, and then restore the main process's + signal handling to be the usual one. """ - LOG.info("Shutting down the WEB server on [%s:%d]", - '[' + listen_address + ']' - if server_clazz is CCSimpleHttpServerIPv6 else listen_address, - port) - http_server.terminate() + signals_to_disable = [signal.SIGINT, signal.SIGTERM] + if sys.platform != "win32": + signals_to_disable += [signal.SIGCHLD, signal.SIGHUP] - # Terminate child processes. - for pp in processes: - pp.terminate() + existing_signal_handlers = {} + for signum in signals_to_disable: + existing_signal_handlers[signum] = signal.signal( + signum, signal.SIG_DFL) + + p = Process(**kwargs) + p.start() + + for signum in signals_to_disable: + signal.signal(signum, existing_signal_handlers[signum]) + + return p + + # Save a process-wide but not shared counter in the main process for how + # many subprocesses of each kind had been spawned, as this will be used in + # the internal naming of the workers. + spawned_api_proc_count: int = 0 + spawned_bg_proc_count: int = 0 + + def spawn_api_process(): + """Starts a single HTTP API worker process for CodeChecker server.""" + nonlocal spawned_api_proc_count + spawned_api_proc_count += 1 + + p = _start_process_with_no_signal_handling( + target=http_server.serve_forever_with_shutdown_handler, + name=f"CodeChecker-API-{spawned_api_proc_count}") + api_processes[cast(int, p.pid)] = p + signal_log(LOG, "DEBUG", f"API handler child process {p.pid} started!") + return p + + LOG.info("Using %d API request handler processes ...", + requested_api_threads) + for _ in range(requested_api_threads): + spawn_api_process() + + def spawn_bg_process(): + """Starts a single Task worker process for CodeChecker server.""" + nonlocal spawned_bg_proc_count + spawned_bg_proc_count += 1 + + p = _start_process_with_no_signal_handling( + target=background_task_executor, + args=(bg_task_queue, + config_sql_server, + is_server_shutting_down, + machine_id, + ), + name=f"CodeChecker-Task-{spawned_bg_proc_count}") + bg_processes[cast(int, p.pid)] = p + signal_log(LOG, "DEBUG", f"Task child process {p.pid} started!") + return p + + LOG.info("Using %d Task handler processes ...", requested_bg_threads) + for _ in range(requested_bg_threads): + spawn_bg_process() + + termination_signal_timestamp = Value('d', 0) + + def forced_termination_signal_handler(signum: int, _frame): + """ + Handle SIGINT (2) and SIGTERM (15) received a second time to stop the + server ungracefully. + """ + if signum not in [signal.SIGINT, signal.SIGTERM]: + signal_log(LOG, "ERROR", "Signal " + f"<{signal.Signals(signum).name} ({signum})> " + "handling attempted by " + "'forced_termination_signal_handler'!") + return + if not is_server_shutting_down.value or \ + abs(termination_signal_timestamp.value) <= \ + sys.float_info.epsilon: + return + if time.time() - termination_signal_timestamp.value <= 2.0: + # Allow some time to pass between the handling of the normal + # termination vs. doing something in the "forced" handler, because + # a human's ^C keypress in a terminal can generate multiple SIGINTs + # in a quick succession. + return + + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + + signal_log(LOG, "WARNING", "Termination signal " + f"<{signal.Signals(signum).name} ({signum})> " + "received a second time, **FORCE** killing the WEB server " + f"on [{http_server.formatted_address}] ...") + + for p in list(api_processes.values()) + list(bg_processes.values()): + try: + p.kill() + except (OSError, ValueError): + pass + # No mercy this time. sys.exit(128 + signum) - def reload_signal_handler(*_args, **_kwargs): + exit_code = Value('B', 0) + + def termination_signal_handler(signum: int, _frame): """ - Reloads server configuration file. + Handle SIGINT (2) and SIGTERM (15) to stop the server gracefully. """ + # Debounce termination signals at this point. + signal.signal(signal.SIGINT, forced_termination_signal_handler) + signal.signal(signal.SIGTERM, forced_termination_signal_handler) + + if is_server_shutting_down.value: + return + if signum not in [signal.SIGINT, signal.SIGTERM]: + signal_log(LOG, "ERROR", "Signal " + f"<{signal.Signals(signum).name} ({signum})> " + "handling attempted by 'termination_signal_handler'!") + return + + is_server_shutting_down.value = True + termination_signal_timestamp.value = time.time() + + exit_code.value = 128 + signum + signal_log(LOG, "INFO", "Shutting down the WEB server on " + f"[{http_server.formatted_address}] ... " + "Please allow some time for graceful clean-up!") + + # Terminate child processes. + # For these subprocesses, let the processes properly clean up after + # themselves in a graceful shutdown scenario. + # For this reason, we fire a bunch of SIGHUPs first, indicating + # that the main server process wants to exit, and then wait for + # the children to die once all of them got the signal. + for pid in api_processes: + try: + signal_log(LOG, "DEBUG", f"SIGINT! API child PID: {pid} ...") + os.kill(pid, signal.SIGINT) + except (OSError, ValueError): + pass + for pid in list(api_processes.keys()): + p = api_processes[pid] + try: + signal_log(LOG, "DEBUG", f"join() API child PID: {pid} ...") + p.join() + p.close() + except (OSError, ValueError): + pass + finally: + del api_processes[pid] + + bg_task_queue.close() + bg_task_queue.join_thread() + for pid in bg_processes: + try: + signal_log(LOG, "DEBUG", f"SIGHUP! Task child PID: {pid} ...") + os.kill(pid, signal.SIGHUP) + except (OSError, ValueError): + pass + for pid in list(bg_processes.keys()): + p = bg_processes[pid] + try: + signal_log(LOG, "DEBUG", f"join() Task child PID: {pid} ...") + p.join() + p.close() + except (OSError, ValueError): + pass + finally: + del bg_processes[pid] + + def reload_signal_handler(signum: int, _frame): + """ + Handle SIGHUP (1) to reload the server's configuration file to memory. + """ + if signum not in [signal.SIGHUP]: + signal_log(LOG, "ERROR", "Signal " + f"<{signal.Signals(signum).name} ({signum})> " + "handling attempted by 'reload_signal_handler'!") + return + + signal_log(LOG, "INFO", + "Received signal to reload server configuration ...") + manager.reload_config() - try: - instance_manager.register(os.getpid(), - os.path.abspath( - context.codechecker_workspace), - port) - except IOError as ex: - LOG.debug(ex.strerror) + signal_log(LOG, "INFO", "Server configuration reload: Done.") - LOG.info("Server waiting for client requests on [%s:%d]", - '[' + listen_address + ']' - if server_clazz is CCSimpleHttpServerIPv6 else listen_address, - port) + sigchild_event_counter = Value('I', 0) + is_already_handling_sigchild = Value('B', False) - def unregister_handler(pid): + def child_signal_handler(signum: int, _frame): """ - Handle errors during instance unregistration. - The workspace might be removed so updating the - config content might fail. + Handle SIGCHLD (17) that signals a child process's interruption or + death by creating a new child to ensure that the requested number of + workers are always alive. """ - try: - instance_manager.unregister(pid) - except IOError as ex: - LOG.debug(ex.strerror) + if is_already_handling_sigchild.value: + # Do not perform this handler recursively to prevent spawning too + # many children. + return + if is_server_shutting_down.value: + # Do not handle SIGCHLD events during normal shutdown, because + # our own subprocess termination calls would fire this handler. + return + if signum not in [signal.SIGCHLD]: + signal_log(LOG, "ERROR", "Signal " + f"<{signal.Signals(signum).name} ({signum})> " + "handling attempted by 'child_signal_handler'!") + return - atexit.register(unregister_handler, os.getpid()) + is_already_handling_sigchild.value = True - for _ in range(manager.worker_processes - 1): - p = multiprocess.Process(target=http_server.serve_forever) - processes.append(p) - p.start() + force_slow_path: bool = False + event_counter: int = sigchild_event_counter.value + if event_counter >= \ + min(requested_api_threads, requested_bg_threads) // 2: + force_slow_path = True + else: + sigchild_event_counter.value = event_counter + 1 - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) + # How many new processes need to be spawned for each type of worker + # process? + spawn_needs: Counter = Counter() + def _check_process_one(kind: str, proclist: Dict[int, Process], + pid: int): + try: + p = proclist[pid] + except KeyError: + return + + # Unfortunately, "Process.is_alive()" cannot be used here, because + # during the handling of SIGCHLD during a child's death, according + # to the state of Python's data structures, the child is still + # alive. + # We run a low-level non-blocking wait again, which will + # immediately return, but properly reap the child process if it has + # terminated. + try: + _, status_signal = os.waitpid(pid, os.WNOHANG) + if status_signal == 0: + # The process is still alive. + return + except ChildProcessError: + pass + + signal_log(LOG, "WARNING", + f"'{kind}' child process (PID {pid}, \"{p.name}\") " + "is not alive anymore!") + spawn_needs[kind] += 1 + + try: + del proclist[pid] + except KeyError: + # Due to the bunching up of signals and that Python runs the + # C-level signals with a custom logic inside the interpreter, + # coupled with the fact that PIDs can be reused, the same PID + # can be reported dead in a quick succession of signals, + # resulting in a KeyError here. + pass + + def _check_processes_many(kind: str, proclist: Dict[int, Process]): + for pid in sorted(proclist.keys()): + _check_process_one(kind, proclist, pid) + + # Try to find the type of the interrupted/dead process based on signal + # information first. + # This should be quicker and more deterministic. + try: + child_pid, child_signal = os.waitpid(-1, os.WNOHANG) + if child_signal == 0: + # Go to the slow path and check the children manually, we did + # not receive a reply from waitpid() with an actual dead child. + raise ChildProcessError() + + _check_process_one("api", api_processes, child_pid) + _check_process_one("background", bg_processes, child_pid) + except ChildProcessError: + # We have not gotten a PID, or it was not found, so we do not know + # who died; in this case, it is better to go on the slow path and + # query all our children individually. + spawn_needs.clear() # Forces the Counter to be empty. + + if force_slow_path: + # A clever sequence of child killings in variously sized batches + # can easily result in missing a few signals here and there, and + # missing a few dead children because 'os.waitpid()' allows us to + # fall into a false "fast path" situation. + # To remedy this, we every so often force a slow path to ensure + # the number of worker processes is as close to the requested + # amount of possible. + + # Forces the Counter to be empty, even if the fast path put an + # entry in there. + spawn_needs.clear() + + if not spawn_needs: + _check_processes_many("api", api_processes) + _check_processes_many("background", bg_processes) + + if force_slow_path: + sigchild_event_counter.value = 0 + signal_log(LOG, "WARNING", + "Too many children died since last full status " + "check, performing one ...") + + # If we came into the handler with a "forced slow path" situation, + # ensure that we spawn enough new processes to backfill the + # missing amount, even if due to the flakyness of signal handling, + # we might not have actually gotten "N" times SIGCHLD firings for + # the death of N children, if they happened in a bundle situation, + # e.g., kill N/4, then kill N/2, then kill 1 or 2, then kill the + # remaining. + spawn_needs["api"] = \ + util.clamp(0, requested_api_threads - len(api_processes), + requested_api_threads) + spawn_needs["background"] = \ + util.clamp(0, requested_bg_threads - len(bg_processes), + requested_bg_threads) + + for kind, num in spawn_needs.items(): + signal_log(LOG, "INFO", + f"(Re-)starting {num} '{kind}' child process(es) ...") + + if kind == "api": + for _ in range(num): + spawn_api_process() + elif kind == "background": + for _ in range(num): + spawn_bg_process() + + is_already_handling_sigchild.value = False + + signal.signal(signal.SIGINT, termination_signal_handler) + signal.signal(signal.SIGTERM, termination_signal_handler) if sys.platform != "win32": + signal.signal(signal.SIGCHLD, child_signal_handler) signal.signal(signal.SIGHUP, reload_signal_handler) - # Main process also acts as a worker. - http_server.serve_forever() + LOG.info("Server waiting for client requests on [%s]", + http_server.formatted_address) + + # We can not use a multiprocessing.Event here because that would result in + # a deadlock, as the process waiting on the event is the one receiving the + # shutdown signal. + while not is_server_shutting_down.value: + time.sleep(5) + + dropped_tasks = _cleanup_incomplete_tasks("Server shut down, task will " + "be never be completed.") + if dropped_tasks: + LOG.info("At server shutdown, dropped %d background tasks that will " + "never be completed.", dropped_tasks) - LOG.info("Webserver quit.") + LOG.info("CodeChecker server quit (main process).") + return exit_code.value def add_initial_run_database(config_sql_server, product_connection): diff --git a/web/server/codechecker_server/session_manager.py b/web/server/codechecker_server/session_manager.py index 276af909cd..662eaa62b0 100644 --- a/web/server/codechecker_server/session_manager.py +++ b/web/server/codechecker_server/session_manager.py @@ -11,16 +11,14 @@ import hashlib import json -import os import re -import uuid from datetime import datetime from typing import Optional from codechecker_common.compatibility.multiprocessing import cpu_count from codechecker_common.logger import get_logger -from codechecker_common.util import load_json +from codechecker_common.util import generate_random_token, load_json from codechecker_web.shared.env import check_file_owner_rw from codechecker_web.shared.version import SESSION_COOKIE_NAME as _SCN @@ -47,29 +45,29 @@ SESSION_COOKIE_NAME = _SCN -def generate_session_token(): - """ - Returns a random session token. - """ - return uuid.UUID(bytes=os.urandom(16)).hex - - def get_worker_processes(scfg_dict): """ Return number of worker processes from the config dictionary. - Return 'worker_processes' field from the config dictionary or returns the - default value if this field is not set or the value is negative. + Return 'worker_processes' and 'background_worker_processes' fields from + the config dictionary or returns the default value if this field is not + set or the value is negative. """ default = cpu_count() - worker_processes = scfg_dict.get('worker_processes', default) + worker_processes = scfg_dict.get("worker_processes", default) + background_worker_processes = scfg_dict.get("background_worker_processes", + default) - if worker_processes < 0: + if not worker_processes or worker_processes < 0: LOG.warning("Number of worker processes can not be negative! Default " "value will be used: %s", default) worker_processes = default + if not background_worker_processes or background_worker_processes < 0: + LOG.warning("Number of task worker processes can not be negative! " + "Default value will be used: %s", worker_processes) + background_worker_processes = worker_processes - return worker_processes + return worker_processes, background_worker_processes class _Session: @@ -182,7 +180,8 @@ def __init__(self, configuration_file, root_sha, force_auth=False): # so it should NOT be handled by session_manager. A separate config # handler for the server's stuff should be created, that can properly # instantiate SessionManager with the found configuration. - self.__worker_processes = get_worker_processes(scfg_dict) + self.__worker_processes, self.__background_worker_processes = \ + get_worker_processes(scfg_dict) self.__max_run_count = scfg_dict.get('max_run_count', None) self.__store_config = scfg_dict.get('store', {}) self.__keepalive_config = scfg_dict.get('keepalive', {}) @@ -328,6 +327,10 @@ def is_enabled(self): def worker_processes(self): return self.__worker_processes + @property + def background_worker_processes(self) -> int: + return self.__background_worker_processes + def get_realm(self): return { "realm": self.__auth_config.get('realm_name'), @@ -622,7 +625,7 @@ def create_session(self, auth_string): return False # Generate a new token and create a local session. - token = generate_session_token() + token = generate_random_token(32) user_name = validation.get('username') groups = validation.get('groups', []) is_root = validation.get('root', False) diff --git a/web/server/codechecker_server/task_executors/__init__.py b/web/server/codechecker_server/task_executors/__init__.py new file mode 100644 index 0000000000..4259749345 --- /dev/null +++ b/web/server/codechecker_server/task_executors/__init__.py @@ -0,0 +1,7 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- diff --git a/web/server/codechecker_server/task_executors/abstract_task.py b/web/server/codechecker_server/task_executors/abstract_task.py new file mode 100644 index 0000000000..34e1cd4d7a --- /dev/null +++ b/web/server/codechecker_server/task_executors/abstract_task.py @@ -0,0 +1,183 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +""" +Contains the base class to be inherited and implemented by all background task +types. +""" +import os +import pathlib +import shutil +from typing import Optional + +from codechecker_common.logger import get_logger + +from ..database.config_db_model import BackgroundTask as DBTask + + +LOG = get_logger("server") + + +class TaskCancelHonoured(Exception): + """ + Specialised tag exception raised by `AbstractTask` implementations in a + checkpoint after having checked that their ``cancel_flag`` was set, in + order to terminate task-specific execution and to register the + cancellation's success by the `AbstractTask.execute` method. + + This exception should **NOT** be caught by user code. + """ + + def __init__(self, task_obj: "AbstractTask"): + super().__init__(f"Task '{task_obj.token}' honoured CANCEL request.") + self.task_obj = task_obj + + +class AbstractTask: + """ + Base class implementing common execution and bookkeeping methods to + facilitate the dispatch of tasks to background worker processes. + + Instances of this class **MUST** be marshallable by ``pickle``, as they + are transported over an IPC `Queue`. + It is important that instances do not grow too large, as the underlying + OS-level primitives of a `Queue` can get full, which can result in a + deadlock situation. + + The run-time contents of the instance should only contain the bare minimum + metadata required for the implementation to execute in the background. + + Implementors of subclasses **MAY REASONABLY ASSUME** that an + `AbstractTask` scheduled in the API handler process of a server will be + actually executed by a background worker in the same process group, on the + same machine instance. + """ + + def __init__(self, token: str, data_path: Optional[pathlib.Path]): + self._token = token + self._data_path = data_path + + @property + def token(self) -> str: + """Returns the task's identifying token, its primary ID.""" + return self._token + + @property + def data_path(self) -> Optional[pathlib.Path]: + """ + Returns the filesystem path where the task's input data is prepared. + """ + return self._data_path + + def destroy_data(self): + """ + Deletes the contents of `data_path`. + """ + if not self._data_path: + return + + try: + shutil.rmtree(self._data_path) + except Exception as ex: + LOG.warning("Failed to remove background task's data_dir at " + "'%s':\n%s", self.data_path, str(ex)) + + def _implementation(self, _task_manager: "TaskManager") -> None: + """ + Implemented by subclasses to perform the logic specific to the task. + + Subclasses should use the `task_manager` object, injected from the + context of the executed subprocess, to query and mutate service-level + information about the current task. + """ + raise NotImplementedError() + + def execute(self, task_manager: "TaskManager") -> None: + """ + Executes the `_implementation` of the task, overridden by subclasses, + to perform a task-specific business logic. + + This high-level wrapper deals with capturing `Exception`s, setting + appropriate status information in the database (through the + injected `task_manager`) and logging failures accordingly. + """ + if task_manager.should_cancel(self): + return + + try: + task_manager._mutate_task_record( + self, lambda dbt: dbt.set_running()) + except KeyError: + # KeyError is thrown if a task without a corresponding database + # record is attempted to be executed. + LOG.error("Failed to execute task '%s' due to database exception", + self.token) + except Exception as ex: + LOG.error("Failed to execute task '%s' due to database exception" + "\n%s", + self.token, str(ex)) + # For any other record, try to set the task abandoned due to an + # exception. + try: + task_manager._mutate_task_record( + self, lambda dbt: + dbt.set_abandoned(force_dropped_status=True)) + except Exception: + return + + LOG.debug("Task '%s' running on machine '%s' executor #%d", + self.token, task_manager.machine_id, os.getpid()) + + try: + self._implementation(task_manager) + LOG.debug("Task '%s' finished on machine '%s' executor #%d", + self.token, + task_manager.machine_id, + os.getpid()) + + try: + task_manager._mutate_task_record( + self, lambda dbt: dbt.set_finished(successfully=True)) + except Exception as ex: + LOG.error("Failed to set task '%s' finished due to " + "database exception:\n%s", + self.token, str(ex)) + except TaskCancelHonoured: + def _log_cancel_and_abandon(db_task: DBTask): + db_task.add_comment("CANCEL!\nCancel request of admin " + "honoured by task.", + "SYSTEM[AbstractTask::execute()]") + db_task.set_abandoned(force_dropped_status=False) + + def _log_drop_and_abandon(db_task: DBTask): + db_task.add_comment("SHUTDOWN!\nTask honoured graceful " + "cancel signal generated by " + "server shutdown.", + "SYSTEM[AbstractTask::execute()]") + db_task.set_abandoned(force_dropped_status=True) + + if not task_manager.is_shutting_down: + task_manager._mutate_task_record(self, _log_cancel_and_abandon) + else: + task_manager._mutate_task_record(self, _log_drop_and_abandon) + except Exception as ex: + LOG.error("Failed to execute task '%s' on machine '%s' " + "executor #%d: %s", + self.token, task_manager.machine_id, os.getpid(), + str(ex)) + import traceback + traceback.print_exc() + + def _log_exception_and_fail(db_task: DBTask): + db_task.add_comment( + f"FAILED!\nException during execution:\n{str(ex)}", + "SYSTEM[AbstractTask::execute()]") + db_task.set_finished(successfully=False) + + task_manager._mutate_task_record(self, _log_exception_and_fail) + finally: + self.destroy_data() diff --git a/web/server/codechecker_server/task_executors/main.py b/web/server/codechecker_server/task_executors/main.py new file mode 100644 index 0000000000..dc9dd4e543 --- /dev/null +++ b/web/server/codechecker_server/task_executors/main.py @@ -0,0 +1,142 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +""" +Implements a dedicated subprocess that deals with running `AbstractTask` +subclasses in the background. +""" +from datetime import timedelta +import os +from queue import Empty +import signal + +from sqlalchemy.orm import sessionmaker + +from codechecker_common.compatibility.multiprocessing import Queue, Value +from codechecker_common.logger import get_logger, signal_log + +from ..database.config_db_model import BackgroundTask as DBTask +from .abstract_task import AbstractTask +from .task_manager import TaskManager + + +WAIT_TIME_FOR_TASK_QUEUE_CLEARING_AT_SERVER_SHUTDOWN = timedelta(seconds=5) + +LOG = get_logger("server") + + +def executor(queue: Queue, + config_db_sql_server, + server_shutdown_flag: "Value", + machine_id: str): + """ + The "main()" function implementation for a background task executor + process. + + This process sets up the state of the local process, and then deals with + popping jobs from the queue and executing them in the local context. + """ + # First things first, a background worker process should NOT respect the + # termination signals received from the parent process, because it has to + # run its own cleanup logic before shutting down. + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + + kill_flag = Value('B', False) + + def executor_hangup_handler(signum: int, _frame): + """ + Handle SIGHUP (1) to do a graceful shutdown of the background worker. + """ + if signum not in [signal.SIGHUP]: + signal_log(LOG, "ERROR", "Signal " + f"<{signal.Signals(signum).name} ({signum})> " + "handling attempted by 'executor_hangup_handler'!") + return + + signal_log(LOG, "DEBUG", f"{os.getpid()}: Received " + f"{signal.Signals(signum).name} ({signum}), preparing for " + "shutdown ...") + kill_flag.value = True + + signal.signal(signal.SIGHUP, executor_hangup_handler) + + config_db_engine = config_db_sql_server.create_engine() + tm = TaskManager(queue, sessionmaker(bind=config_db_engine), kill_flag, + machine_id) + + while not kill_flag.value: + try: + # Do not block indefinitely when waiting for a job, to allow + # checking whether the kill flags were set. + t: AbstractTask = queue.get(block=True, timeout=1) + except Empty: + continue + + import pprint + LOG.info("Executor #%d received task object:\n\n%s:\n%s\n\n", + os.getpid(), t, pprint.pformat(t.__dict__)) + + t.execute(tm) + + # Once the main loop of task execution process has finished, there might + # still be tasks left in the queue. + # If the server is shutting down (this is distinguished from the local kill + # flag, because a 'SIGHUP' might arrive from any source, not just a valid + # graceful shutdown!), then these jobs would be lost if the process just + # exited, with no information reported to the database. + # We need set these tasks to dropped as much as possible. + def _log_shutdown_and_abandon(db_task: DBTask): + db_task.add_comment("SHUTDOWN!\nTask never started due to the " + "server shutdown!", "SYSTEM") + db_task.set_abandoned(force_dropped_status=True) + + def _drop_task_at_shutdown(t: AbstractTask): + try: + LOG.debug("Dropping task '%s' due to server shutdown...", t.token) + tm._mutate_task_record(t, _log_shutdown_and_abandon) + except Exception: + pass + finally: + t.destroy_data() + + if server_shutdown_flag.value: + # Unfortunately, it is not guaranteed which process will wake up first + # when popping objects from the queue. + # Blocking indefinitely would not be a solution here, because all + # producers (API threads) had likely already exited at this point. + # However, simply observing no elements for a short period of time is + # also not enough, as at the very last moments of a server's lifetime, + # one process might observe the queue to be empty, simply because + # another process stole the object that was put into it. + # + # To be on the safe side of things, we require to observe the queue to + # be *constantly* empty over a longer period of repetitive sampling. + empty_sample_count: int = 0 + while empty_sample_count < int( + WAIT_TIME_FOR_TASK_QUEUE_CLEARING_AT_SERVER_SHUTDOWN + .total_seconds()): + try: + t: AbstractTask = queue.get(block=True, timeout=1) + except Empty: + empty_sample_count += 1 + continue + + empty_sample_count = 0 + _drop_task_at_shutdown(t) + + queue.close() + queue.join_thread() + + try: + config_db_engine.dispose() + except Exception as ex: + LOG.error("Failed to shut down task executor!\n%s", str(ex)) + return + + LOG.debug("Task executor subprocess PID %d exited main loop.", + os.getpid()) diff --git a/web/server/codechecker_server/task_executors/task_manager.py b/web/server/codechecker_server/task_executors/task_manager.py new file mode 100644 index 0000000000..ddd3b31053 --- /dev/null +++ b/web/server/codechecker_server/task_executors/task_manager.py @@ -0,0 +1,229 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +""" +Contains status management and query methods to handle bookkeeping for +dispatched background tasks. +""" +import os +from pathlib import Path +import tempfile +from typing import Callable, Optional + +import sqlalchemy + +from codechecker_common.compatibility.multiprocessing import Queue, Value +from codechecker_common.logger import get_logger, signal_log +from codechecker_common.util import generate_random_token + +from ..database.config_db_model import BackgroundTask as DBTask, Product +from ..database.database import DBSession + +MAX_TOKEN_RANDOM_RETRIES = 10 + +LOG = get_logger("server") + + +class ExecutorInProgressShutdownError(Exception): + """ + Exception raised to indicate that the background executors are under + shutdown. + """ + def __init__(self): + super().__init__("Task executor is shutting down!") + + +class TaskManager: + """ + Handles the creation of "Task" status objects in the database and pushing + in-memory `AbstractTask` subclass instances to a `Queue`. + + This class is instantiatied for EVERY WORKER separately, and is not a + shared resource! + """ + + def __init__(self, q: Queue, config_db_session_factory, + executor_kill_flag: Value, machine_id: str): + self._queue = q + self._database_factory = config_db_session_factory + self._is_shutting_down = executor_kill_flag + self._machine_id = machine_id + + @property + def machine_id(self) -> str: + """Returns the ``machine_id`` the instance was constructed with.""" + return self._machine_id + + def allocate_task_record(self, kind: str, summary: str, + user_name: Optional[str], + product: Optional[Product] = None) -> str: + """ + Creates the token and the status record for a new task with the given + initial metadata. + + Returns the token of the task, which is a unique identifier of the + allocated record. + """ + try_count: int = 0 + while True: + with DBSession(self._database_factory) as session: + try: + token = generate_random_token(DBTask._token_length) + + task = DBTask(token, kind, summary, self.machine_id, + user_name, product) + session.add(task) + session.commit() + + return token + except sqlalchemy.exc.IntegrityError as ie: + # The only failure that can happen is the PRIMARY KEY's + # UNIQUE violation, which means we hit jackpot by + # generating an already used token! + try_count += 1 + + if try_count >= MAX_TOKEN_RANDOM_RETRIES: + raise KeyError( + "Failed to generate a unique ID for task " + f"{kind} ({summary}) after " + f"{MAX_TOKEN_RANDOM_RETRIES} retries!") from ie + + def create_task_data(self, token: str) -> Path: + """ + Creates a temporary directory which is **NOT** cleaned up + automatically, and suitable for putting arbitrary files underneath + to communicate large inputs (that should not be put in the `Queue`) + to the `execute` method of an `AbstractTask`. + """ + task_tmp_root = Path(tempfile.gettempdir()) / "codechecker_tasks" \ + / self.machine_id + os.makedirs(task_tmp_root, exist_ok=True) + + task_tmp_dir = tempfile.mkdtemp(prefix=f"{token}-") + return Path(task_tmp_dir) + + def _get_task_record(self, task_obj: "AbstractTask") -> DBTask: + """ + Retrieves the `DBTask` for the task identified by `task_obj`. + + This class should not be mutated, only the fields queried. + """ + with DBSession(self._database_factory) as session: + try: + db_task = session.query(DBTask).get(task_obj.token) + session.expunge(db_task) + return db_task + except sqlalchemy.exc.SQLAlchemyError as sql_err: + raise KeyError(f"No task record for token '{task_obj.token}' " + "in the database") from sql_err + + def _mutate_task_record(self, task_obj: "AbstractTask", + mutator: Callable[[DBTask], None]): + """ + Executes the given `mutator` function for the `DBTask` record + corresponding to the `task_obj` description available in memory. + """ + with DBSession(self._database_factory) as session: + try: + db_record = session.query(DBTask).get(task_obj.token) + except sqlalchemy.exc.SQLAlchemyError as sql_err: + raise KeyError(f"No task record for token '{task_obj.token}' " + "in the database") from sql_err + + try: + mutator(db_record) + except Exception: + session.rollback() + + import traceback + traceback.print_exc() + raise + + session.commit() + + def push_task(self, task_obj: "AbstractTask"): + """Enqueues the given `task_obj` onto the `Queue`.""" + if self.is_shutting_down: + raise ExecutorInProgressShutdownError() + + # Note, that the API handler process calling push_task() might be + # killed before writing to the queue, so an actually enqueued task + # (according to the DB) might never be consumed by a background + # process. + # As we have to COMMIT the status change before the actual processing + # in order to show the time stamp to the user(s), there is no better + # way to make this more atomic. + try: + self._mutate_task_record(task_obj, lambda dbt: dbt.set_enqueued()) + self._queue.put(task_obj) + except SystemExit as sex: + try: + signal_log(LOG, "WARNING", f"Process #{os.getpid()}: " + "push_task() killed via SystemExit during " + f"enqueue of task '{task_obj.token}'!") + + def _log_and_abandon(db_task: DBTask): + db_task.add_comment( + "SHUTDOWN!\nEnqueueing process terminated during the " + "ongoing enqueue! The task will never be executed!", + "SYSTEM[TaskManager::push_task()]") + db_task.set_abandoned(force_dropped_status=True) + + self._mutate_task_record(task_obj, _log_and_abandon) + finally: + raise sex + + @property + def is_shutting_down(self) -> bool: + """ + Returns whether the shutdown flag for the executor associated with the + `TaskManager` had been set. + """ + return self._is_shutting_down.value + + def should_cancel(self, task_obj: "AbstractTask") -> bool: + """ + Returns whether the task identified by `task_obj` should be + co-operatively cancelled. + """ + db_task = self._get_task_record(task_obj) + return self.is_shutting_down or \ + (db_task.status in ["enqueued", "running"] + and db_task.cancel_flag) + + def heartbeat(self, task_obj: "AbstractTask"): + """ + Triggers ``heartbeat()`` timestamp update in the database for + `task_obj`. + """ + self._mutate_task_record(task_obj, lambda dbt: dbt.heartbeat()) + + +def drop_all_incomplete_tasks(config_db_session_factory, machine_id: str, + action: str) -> int: + """ + Sets all tasks in the database (reachable via `config_db_session_factory`) + that were associated with the given `machine_id` to ``"dropped"`` status, + indicating that the status was changed during the `action`. + + Returns the number of `DBTask`s actually changed. + """ + count: int = 0 + with DBSession(config_db_session_factory) as session: + for t in session.query(DBTask) \ + .filter(DBTask.machine_id == machine_id, + DBTask.status.in_(["allocated", + "enqueued", + "running"])) \ + .all(): + count += 1 + t.add_comment(f"DROPPED!\n{action}", + "SYSTEM") + t.set_abandoned(force_dropped_status=True) + + session.commit() + return count diff --git a/web/server/codechecker_server/tmp.py b/web/server/codechecker_server/tmp.py deleted file mode 100644 index bbc5e77bea..0000000000 --- a/web/server/codechecker_server/tmp.py +++ /dev/null @@ -1,37 +0,0 @@ -# ------------------------------------------------------------------------- -# -# Part of the CodeChecker project, under the Apache License v2.0 with -# LLVM Exceptions. See LICENSE for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# -# ------------------------------------------------------------------------- -""" -Temporary directory module. -""" - - -import datetime -import hashlib -import os - - -from codechecker_common.logger import get_logger - -LOG = get_logger('system') - - -def get_tmp_dir_hash(): - """Generate a hash based on the current time and process id.""" - - pid = os.getpid() - time = datetime.datetime.now() - - data = str(pid) + str(time) - - dir_hash = hashlib.md5() - dir_hash.update(data.encode("utf-8")) - - LOG.debug('The generated temporary directory hash is %s.', - dir_hash.hexdigest()) - - return dir_hash.hexdigest() diff --git a/web/server/config/server_config.json b/web/server/config/server_config.json index e42745f08d..a5ad4999c8 100644 --- a/web/server/config/server_config.json +++ b/web/server/config/server_config.json @@ -1,4 +1,6 @@ { + "background_worker_processes": null, + "worker_processes": null, "max_run_count": null, "store": { "analysis_statistics_dir": null, diff --git a/web/server/vue-cli/package-lock.json b/web/server/vue-cli/package-lock.json index d908b8c278..e15bf5228e 100644 --- a/web/server/vue-cli/package-lock.json +++ b/web/server/vue-cli/package-lock.json @@ -11,7 +11,7 @@ "@mdi/font": "^6.5.95", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", - "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz", + "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz", "codemirror": "^5.65.0", "date-fns": "^2.28.0", "js-cookie": "^3.0.1", @@ -5113,9 +5113,9 @@ } }, "node_modules/codechecker-api": { - "version": "6.58.0", - "resolved": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz", - "integrity": "sha512-N6qK5cnLt32jnJlSyyGMmW6FCzybDljyH1RrGOZ1Gk9n1vV7WluJbC9InYWsZ5lbK7xVyIrphTKXhqC4ARKF6g==", + "version": "6.59.0", + "resolved": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz", + "integrity": "sha512-9C/nsRU6vx9JO+Y1PPMCslXE3N1uokTNU5cwTLkCx1g3VKKpFRJttp1Q0yPE+thqp/xIHxzTKuwCi+2H0SPvGg==", "license": "SEE LICENSE IN LICENSE", "dependencies": { "thrift": "0.13.0-hotfix.1" @@ -21145,8 +21145,8 @@ "dev": true }, "codechecker-api": { - "version": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz", - "integrity": "sha512-N6qK5cnLt32jnJlSyyGMmW6FCzybDljyH1RrGOZ1Gk9n1vV7WluJbC9InYWsZ5lbK7xVyIrphTKXhqC4ARKF6g==", + "version": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz", + "integrity": "sha512-9C/nsRU6vx9JO+Y1PPMCslXE3N1uokTNU5cwTLkCx1g3VKKpFRJttp1Q0yPE+thqp/xIHxzTKuwCi+2H0SPvGg==", "requires": { "thrift": "0.13.0-hotfix.1" } diff --git a/web/server/vue-cli/package.json b/web/server/vue-cli/package.json index 2239777668..f31789b897 100644 --- a/web/server/vue-cli/package.json +++ b/web/server/vue-cli/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "@mdi/font": "^6.5.95", - "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.58.0.tgz", + "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.59.0.tgz", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", "codemirror": "^5.65.0", diff --git a/web/tests/functional/instance_manager/test_instances.py b/web/tests/functional/instance_manager/test_instances.py index 0e7fc3a1d6..0851548100 100644 --- a/web/tests/functional/instance_manager/test_instances.py +++ b/web/tests/functional/instance_manager/test_instances.py @@ -10,7 +10,6 @@ Instance manager tests. """ - import os import shutil import subprocess @@ -178,7 +177,7 @@ def test_shutdown_record_keeping(self): EVENT_2.set() # Give the server some grace period to react to the kill command. - time.sleep(5) + time.sleep(30) test_cfg = env.import_test_cfg(self._test_workspace) codechecker_1 = test_cfg['codechecker_1'] diff --git a/web/tests/functional/tasks/__init__.py b/web/tests/functional/tasks/__init__.py new file mode 100644 index 0000000000..4259749345 --- /dev/null +++ b/web/tests/functional/tasks/__init__.py @@ -0,0 +1,7 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- diff --git a/web/tests/functional/tasks/test_task_management.py b/web/tests/functional/tasks/test_task_management.py new file mode 100644 index 0000000000..bf6f968dca --- /dev/null +++ b/web/tests/functional/tasks/test_task_management.py @@ -0,0 +1,484 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +""" +Contains tests of the ``"/Tasks"`` API endpoint to query, using the +``DummyTask``, normal task management related API functions. +""" +from copy import deepcopy +from datetime import datetime, timezone +import os +import pathlib +import shutil +import unittest +import time +from typing import List, Optional, cast + +import multiprocess + +from codechecker_api_shared.ttypes import RequestFailed, Ternary +from codechecker_api.codeCheckerServersideTasks_v6.ttypes import \ + AdministratorTaskInfo, TaskFilter, TaskInfo, TaskStatus + +from libtest import codechecker, env + + +# Stop events for the CodeChecker servers. +STOP_SERVER = multiprocess.Event() +STOP_SERVER_AUTH = multiprocess.Event() +STOP_SERVER_NO_AUTH = multiprocess.Event() + +TEST_WORKSPACE: Optional[str] = None + + +# Note: Test names in this file follow a strict ordinal convention, because +# the assertions are created with a specific execution history! + +class TaskManagementAPITests(unittest.TestCase): + def setup_class(self): + global TEST_WORKSPACE + TEST_WORKSPACE = env.get_workspace("tasks") + os.environ["TEST_WORKSPACE"] = TEST_WORKSPACE + + codechecker_cfg = { + "check_env": env.test_env(TEST_WORKSPACE), + "workspace": TEST_WORKSPACE, + "checkers": [], + "viewer_host": "localhost", + "viewer_port": env.get_free_port(), + "viewer_product": "tasks", + } + + # Run a normal server that is only used to manage the + # "test_package_product". + codechecker.start_server(codechecker_cfg, STOP_SERVER, + ["--machine-id", "workspace-manager"]) + + codechecker_cfg_no_auth = deepcopy(codechecker_cfg) + codechecker_cfg_no_auth.update({ + "viewer_port": env.get_free_port(), + }) + + # Run a normal server which does not require authentication. + codechecker.start_server(codechecker_cfg_no_auth, STOP_SERVER_NO_AUTH, + ["--machine-id", "unprivileged"]) + + codechecker_cfg_auth = deepcopy(codechecker_cfg) + codechecker_cfg_auth.update({ + "viewer_port": env.get_free_port(), + }) + + # Run a privileged server which does require authentication. + (pathlib.Path(TEST_WORKSPACE) / "root.user").unlink() + env.enable_auth(TEST_WORKSPACE) + codechecker.start_server(codechecker_cfg_auth, STOP_SERVER_AUTH, + ["--machine-id", "privileged"]) + + env.export_test_cfg(TEST_WORKSPACE, + {"codechecker_cfg": codechecker_cfg, + "codechecker_cfg_no_auth": + codechecker_cfg_no_auth, + "codechecker_cfg_auth": codechecker_cfg_auth}) + + codechecker.add_test_package_product(codechecker_cfg, TEST_WORKSPACE) + + def teardown_class(self): + # TODO: If environment variable is set keep the workspace and print + # out the path. + global TEST_WORKSPACE + + STOP_SERVER_NO_AUTH.set() + STOP_SERVER_NO_AUTH.clear() + STOP_SERVER_AUTH.set() + STOP_SERVER_AUTH.clear() + + codechecker.remove_test_package_product(TEST_WORKSPACE) + STOP_SERVER.set() + STOP_SERVER.clear() + + print(f"Removing: {TEST_WORKSPACE}") + shutil.rmtree(cast(str, TEST_WORKSPACE), ignore_errors=True) + + def setup_method(self, _): + test_workspace = os.environ["TEST_WORKSPACE"] + self._test_env = env.import_test_cfg(test_workspace) + + print(f"Running {self.__class__.__name__} tests in {test_workspace}") + + auth_server = self._test_env["codechecker_cfg_auth"] + no_auth_server = self._test_env["codechecker_cfg_no_auth"] + + self._auth_client = env.setup_auth_client(test_workspace, + auth_server["viewer_host"], + auth_server["viewer_port"]) + + root_token = self._auth_client.performLogin("Username:Password", + "root:root") + admin_token = self._auth_client.performLogin("Username:Password", + "admin:admin123") + + self._anonymous_task_client = env.setup_task_client( + test_workspace, + no_auth_server["viewer_host"], no_auth_server["viewer_port"]) + self._admin_task_client = env.setup_task_client( + test_workspace, + auth_server["viewer_host"], auth_server["viewer_port"], + session_token=admin_token) + self._privileged_task_client = env.setup_task_client( + test_workspace, + auth_server["viewer_host"], auth_server["viewer_port"], + session_token=root_token) + + def test_task_1_query_status(self): + task_token = self._anonymous_task_client.createDummyTask(10, False) + + time.sleep(5) + task_info: TaskInfo = self._anonymous_task_client.getTaskInfo( + task_token) + self.assertEqual(task_info.token, task_token) + self.assertEqual(task_info.status, + TaskStatus._NAMES_TO_VALUES["RUNNING"]) + self.assertEqual(task_info.productId, 0) + self.assertIsNone(task_info.actorUsername) + self.assertIn("Dummy task", task_info.summary) + self.assertEqual(task_info.cancelFlagSet, False) + + time.sleep(10) # A bit more than exactly what remains of 10 seconds! + task_info = self._anonymous_task_client.getTaskInfo(task_token) + self.assertEqual(task_info.status, + TaskStatus._NAMES_TO_VALUES["COMPLETED"]) + self.assertEqual(task_info.cancelFlagSet, False) + self.assertIsNotNone(task_info.enqueuedAtEpoch) + self.assertIsNotNone(task_info.startedAtEpoch) + self.assertLessEqual(task_info.enqueuedAtEpoch, + task_info.startedAtEpoch) + self.assertIsNotNone(task_info.completedAtEpoch) + self.assertLess(task_info.startedAtEpoch, task_info.completedAtEpoch) + self.assertEqual(task_info.cancelFlagSet, False) + + def test_task_2_query_status_of_failed(self): + task_token = self._anonymous_task_client.createDummyTask(10, True) + + time.sleep(5) + task_info: TaskInfo = self._anonymous_task_client.getTaskInfo( + task_token) + self.assertEqual(task_info.token, task_token) + self.assertEqual(task_info.status, + TaskStatus._NAMES_TO_VALUES["RUNNING"]) + self.assertEqual(task_info.cancelFlagSet, False) + + time.sleep(10) # A bit more than exactly what remains of 10 seconds! + task_info = self._anonymous_task_client.getTaskInfo(task_token) + self.assertEqual(task_info.status, + TaskStatus._NAMES_TO_VALUES["FAILED"]) + self.assertEqual(task_info.cancelFlagSet, False) + + def test_task_3_cancel(self): + task_token = self._anonymous_task_client.createDummyTask(10, False) + + time.sleep(3) + cancel_req: bool = self._privileged_task_client.cancelTask(task_token) + self.assertTrue(cancel_req) + + time.sleep(3) + cancel_req_2: bool = self._privileged_task_client.cancelTask( + task_token) + # The task was already cancelled, so cancel_req_2 is not the API call + # that cancelled the task. + self.assertFalse(cancel_req_2) + + time.sleep(5) # A bit more than exactly what remains of 10 seconds! + task_info: TaskInfo = self._anonymous_task_client.getTaskInfo( + task_token) + self.assertEqual(task_info.status, + TaskStatus._NAMES_TO_VALUES["CANCELLED"]) + self.assertEqual(task_info.cancelFlagSet, True) + self.assertIn("root", task_info.comments) + self.assertIn("SUPERUSER requested cancellation.", task_info.comments) + self.assertIn("CANCEL!\nCancel request of admin honoured by task.", + task_info.comments) + self.assertIsNotNone(task_info.enqueuedAtEpoch) + self.assertIsNotNone(task_info.startedAtEpoch) + self.assertLessEqual(task_info.enqueuedAtEpoch, + task_info.startedAtEpoch) + self.assertIsNotNone(task_info.completedAtEpoch) + self.assertLess(task_info.startedAtEpoch, task_info.completedAtEpoch) + + def test_task_4_get_tasks_as_admin(self): + with self.assertRaises(RequestFailed): + self._admin_task_client.getTasks(TaskFilter( + # No SUPERUSER rights of test admin. + productIDs=[], + )) + with self.assertRaises(RequestFailed): + self._admin_task_client.getTasks(TaskFilter( + # Default product, no PRODUCT_ADMIN rights of test admin. + productIDs=[1] + )) + + # PRODUCT_ADMIN rights on test-specific product... + task_infos: List[AdministratorTaskInfo] = \ + self._admin_task_client.getTasks(TaskFilter(productIDs=[2])) + # ... but no product-specific tasks exist in this test suite. + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter()) + self.assertEqual(len(task_infos), 3) + + self.assertEqual(sum(1 for t in task_infos + if t.normalInfo.status == + TaskStatus._NAMES_TO_VALUES["COMPLETED"]), 1) + self.assertEqual(sum(1 for t in task_infos + if t.normalInfo.status == + TaskStatus._NAMES_TO_VALUES["FAILED"]), 1) + self.assertEqual(sum(1 for t in task_infos + if t.normalInfo.status == + TaskStatus._NAMES_TO_VALUES["CANCELLED"]), 1) + + def test_task_5_info_query_filters(self): + current_time_epoch = int(datetime.now(timezone.utc).timestamp()) + + task_infos: List[AdministratorTaskInfo] = \ + self._privileged_task_client.getTasks(TaskFilter( + machineIDs=["nonexistent"] + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + machineIDs=["unprivileged"] + )) + self.assertEqual(len(task_infos), 3) + + tokens_from_previous_test = [t.normalInfo.token for t in task_infos] + + task_infos = self._admin_task_client.getTasks(TaskFilter( + tokens=tokens_from_previous_test + )) + # Admin client is not a SUPERUSER, it should not get the list of + # tasks visible only to superusers because they are "server-level". + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + machineIDs=["privileged"] + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + startedBeforeEpoch=current_time_epoch + )) + self.assertEqual(len(task_infos), 3) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + startedAfterEpoch=current_time_epoch + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + cancelFlag=Ternary._NAMES_TO_VALUES["ON"] + )) + self.assertEqual(len(task_infos), 1) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + cancelFlag=Ternary._NAMES_TO_VALUES["OFF"] + )) + self.assertEqual(len(task_infos), 2) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + consumedFlag=Ternary._NAMES_TO_VALUES["ON"] + )) + self.assertEqual(len(task_infos), 3) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + consumedFlag=Ternary._NAMES_TO_VALUES["OFF"] + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter()) + + current_time_epoch = int(datetime.now(timezone.utc).timestamp()) + for i in range(10): + target_api = self._anonymous_task_client if i % 2 == 0 \ + else self._admin_task_client + for j in range(10): + target_api.createDummyTask(1, bool(j % 2 == 0)) + + task_infos = self._privileged_task_client.getTasks(TaskFilter()) + self.assertEqual(len(task_infos), 103) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + )) + self.assertEqual(len(task_infos), 100) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + machineIDs=["unprivileged"] + )) + self.assertEqual(len(task_infos), 50) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + machineIDs=["privileged"] + )) + self.assertEqual(len(task_infos), 50) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + usernames=[], + )) + self.assertEqual(len(task_infos), 50) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + usernames=["admin"], + )) + self.assertEqual(len(task_infos), 50) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + usernames=["root"], + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch + )) + # Some tasks ought to have started at least. + self.assertGreater(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch + )) + # Some tasks ought to have also finished at least. + self.assertGreater(len(task_infos), 0) + + # Let every task terminate. We should only need 1 second per task, + # running likely in a multithreaded environment. + # Let's have some leeway, though... + time.sleep(2 * (100 * 1 // cast(int, os.cpu_count()))) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch + )) + # All tasks should have finished. + self.assertEqual(len(task_infos), 100) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch, + statuses=[TaskStatus._NAMES_TO_VALUES["COMPLETED"]] + )) + self.assertEqual(len(task_infos), 50) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch, + statuses=[TaskStatus._NAMES_TO_VALUES["FAILED"]] + )) + self.assertEqual(len(task_infos), 50) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch, + cancelFlag=Ternary._NAMES_TO_VALUES["ON"] + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch, + consumedFlag=Ternary._NAMES_TO_VALUES["ON"] + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + machineIDs=["*privileged"] + )) + self.assertEqual(len(task_infos), 103) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + kinds=["*Dummy*"] + )) + self.assertEqual(len(task_infos), 103) + + # Try to consume the task status from the wrong user! + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedAfterEpoch=current_time_epoch, + completedAfterEpoch=current_time_epoch, + usernames=[], + statuses=[TaskStatus._NAMES_TO_VALUES["COMPLETED"]] + )) + self.assertEqual(len(task_infos), 25) + a_token: str = task_infos[0].normalInfo.token + with self.assertRaises(RequestFailed): + self._admin_task_client.getTaskInfo(a_token) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + machineIDs=["workspace-manager"] + )) + self.assertEqual(len(task_infos), 0) + + def test_task_6_dropping(self): + current_time_epoch = int(datetime.now(timezone.utc).timestamp()) + many_task_count = 4 * cast(int, os.cpu_count()) + for _ in range(many_task_count): + self._anonymous_task_client.createDummyTask(600, False) + + STOP_SERVER_NO_AUTH.set() + time.sleep(30) + STOP_SERVER_NO_AUTH.clear() + after_shutdown_time_epoch = int(datetime.now(timezone.utc) + .timestamp()) + + task_infos: List[AdministratorTaskInfo] = \ + self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + statuses=[ + TaskStatus._NAMES_TO_VALUES["ENQUEUED"], + TaskStatus._NAMES_TO_VALUES["RUNNING"], + TaskStatus._NAMES_TO_VALUES["COMPLETED"], + TaskStatus._NAMES_TO_VALUES["FAILED"], + TaskStatus._NAMES_TO_VALUES["CANCELLED"] + ] + )) + self.assertEqual(len(task_infos), 0) + + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + statuses=[TaskStatus._NAMES_TO_VALUES["DROPPED"]], + # System-level dropping is not a "cancellation" action! + cancelFlag=Ternary._NAMES_TO_VALUES["OFF"] + )) + self.assertEqual(len(task_infos), many_task_count) + dropped_task_infos = {ti.normalInfo.token: ti for ti in task_infos} + + # Some tasks will have started, and the server pulled out from under. + task_infos = self._privileged_task_client.getTasks(TaskFilter( + enqueuedAfterEpoch=current_time_epoch, + startedBeforeEpoch=after_shutdown_time_epoch, + statuses=[TaskStatus._NAMES_TO_VALUES["DROPPED"]] + )) + for ti in task_infos: + self.assertIn("SHUTDOWN!\nTask honoured graceful cancel signal " + "generated by server shutdown.", + ti.normalInfo.comments) + del dropped_task_infos[ti.normalInfo.token] + + # The rest could have never started. + for ti in dropped_task_infos.values(): + self.assertTrue("DROPPED!\n" in ti.normalInfo.comments or + "SHUTDOWN!\n" in ti.normalInfo.comments) diff --git a/web/tests/libtest/env.py b/web/tests/libtest/env.py index 1610db8bef..f89e495992 100644 --- a/web/tests/libtest/env.py +++ b/web/tests/libtest/env.py @@ -18,16 +18,18 @@ import socket import stat import subprocess +from typing import cast from codechecker_common.util import load_json -from .thrift_client_to_db import get_auth_client -from .thrift_client_to_db import get_config_client -from .thrift_client_to_db import get_product_client -from .thrift_client_to_db import get_viewer_client +from .thrift_client_to_db import \ + get_auth_client, \ + get_config_client, \ + get_product_client, \ + get_task_client, \ + get_viewer_client -from functional import PKG_ROOT -from functional import REPO_ROOT +from functional import PKG_ROOT, REPO_ROOT def get_free_port(): @@ -236,6 +238,30 @@ def setup_config_client(workspace, session_token=session_token, protocol=proto) +def setup_task_client(workspace, + host=None, port=None, + uri="/Tasks", + auto_handle_connection=True, + session_token=None, + protocol="http"): + if not host and not port: + codechecker_cfg = import_test_cfg(workspace)["codechecker_cfg"] + port = codechecker_cfg["viewer_port"] + host = codechecker_cfg["viewer_host"] + + if session_token is None: + session_token = get_session_token(workspace, host, port) + if session_token == "_PROHIBIT": + session_token = None + + return get_task_client(port=port, + host=cast(str, host), + uri=uri, + auto_handle_connection=auto_handle_connection, + session_token=session_token, + protocol=protocol) + + def repository_root(): return os.path.abspath(os.environ['REPO_ROOT']) diff --git a/web/tests/libtest/thrift_client_to_db.py b/web/tests/libtest/thrift_client_to_db.py index de7788c929..2b5c5a11e8 100644 --- a/web/tests/libtest/thrift_client_to_db.py +++ b/web/tests/libtest/thrift_client_to_db.py @@ -238,6 +238,26 @@ def __getattr__(self, attr): return partial(self._thrift_client_call, attr) +class CCTaskHelper(ThriftAPIHelper): + def __init__(self, proto, host, port, uri, auto_handle_connection=True, + session_token=None): + from codechecker_api.codeCheckerServersideTasks_v6 \ + import codeCheckerServersideTaskService + from codechecker_client.credential_manager import SESSION_COOKIE_NAME + + url = create_product_url(proto, host, port, f"/v{VERSION}{uri}") + transport = THttpClient.THttpClient(url) + protocol = TJSONProtocol.TJSONProtocol(transport) + client = codeCheckerServersideTaskService.Client(protocol) + if session_token: + headers = {'Cookie': f"{SESSION_COOKIE_NAME}={session_token}"} + transport.setCustomHeaders(headers) + super().__init__(transport, client, auto_handle_connection) + + def __getattr__(self, attr): + return partial(self._thrift_client_call, attr) + + def get_all_run_results( client, run_id=None, @@ -303,3 +323,10 @@ def get_config_client(port, host='localhost', uri='/Configuration', return CCConfigHelper(protocol, host, port, uri, auto_handle_connection, session_token) + + +def get_task_client(port, host="localhost", uri="/Tasks", + auto_handle_connection=True, session_token=None, + protocol="http"): + return CCTaskHelper(protocol, host, port, uri, auto_handle_connection, + session_token)