From 10473e4c0711c455ac0beeb143c88b67a06f6023 Mon Sep 17 00:00:00 2001 From: Mikolaj Kucharski Date: Tue, 17 Jun 2025 18:04:36 +0000 Subject: [PATCH 1/5] Fix check_ssl_connection() function Function doesn't wait for `openssl s_client ...` to finish. It assumes that when the command is still running that is the successful condition. However the function should wait for exit code from the binary. We saw in production intermittent and very often `skale ssl upload` failures. This change should fix this problem and underlying race condition. --- node_cli/core/ssl/check.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/node_cli/core/ssl/check.py b/node_cli/core/ssl/check.py index d512c777..3c8593bc 100644 --- a/node_cli/core/ssl/check.py +++ b/node_cli/core/ssl/check.py @@ -201,8 +201,15 @@ def check_ssl_connection(host, port, silent=False): ] expose_output = not silent with detached_subprocess(ssl_check_cmd, expose_output=expose_output) as dp: - time.sleep(1) - code = dp.poll() - if code is not None: + for _ in range(10): + code = dp.poll() + if code is None: + logger.info('Healthcheck process still running...') + time.sleep(2) + continue + elif code == 0: + return logger.error('Healthcheck connection failed') raise SSLHealthcheckError('OpenSSL connection verification failed') + logger.error('Healthcheck timed-out') + raise SSLHealthcheckError('OpenSSL connection verification timed-out') From 533044c08f1116a2822de0233ecc522aeb933d52 Mon Sep 17 00:00:00 2001 From: Mikolaj Kucharski Date: Mon, 23 Jun 2025 18:55:53 +0000 Subject: [PATCH 2/5] Move to dp.wait() in check_ssl_connection() Replace for loop and dp.poll() with more straightforward dp.wait() with a timeout, as requested during diff review. --- node_cli/core/ssl/check.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/node_cli/core/ssl/check.py b/node_cli/core/ssl/check.py index 3c8593bc..a5c7a0a5 100644 --- a/node_cli/core/ssl/check.py +++ b/node_cli/core/ssl/check.py @@ -20,6 +20,7 @@ import time import socket import logging +import subprocess from contextlib import contextmanager from node_cli.core.ssl.utils import detached_subprocess @@ -201,15 +202,15 @@ def check_ssl_connection(host, port, silent=False): ] expose_output = not silent with detached_subprocess(ssl_check_cmd, expose_output=expose_output) as dp: - for _ in range(10): - code = dp.poll() - if code is None: - logger.info('Healthcheck process still running...') - time.sleep(2) - continue - elif code == 0: - return - logger.error('Healthcheck connection failed') - raise SSLHealthcheckError('OpenSSL connection verification failed') - logger.error('Healthcheck timed-out') - raise SSLHealthcheckError('OpenSSL connection verification timed-out') + timeout = 20 + try: + dp.wait(timeout=timeout) + except subprocess.TimeoutExpired: + logger.error('Healthcheck timed-out after %s s', timeout) + raise SSLHealthcheckError('OpenSSL connection verification timed-out') + + if dp.returncode == 0: # success + return + + logger.error('Healthcheck connection failed (code %s)', dp.returncode) + raise SSLHealthcheckError('OpenSSL connection verification failed') From 230835c794733e2a27a787f05f57928e8ec56149 Mon Sep 17 00:00:00 2001 From: Mikolaj Kucharski Date: Mon, 23 Jun 2025 20:25:04 +0000 Subject: [PATCH 3/5] Read from /dev/null in detached_subprocess() Redirect the child's standard input to subprocess.DEVNULL, so it starts with no stdin attached. This prevents the OpenSSL health-check process from reading from, or blocking on, the parent's terminal or execution environment stdin stream. --- node_cli/core/ssl/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node_cli/core/ssl/utils.py b/node_cli/core/ssl/utils.py index a2e71e1f..c11ebda8 100644 --- a/node_cli/core/ssl/utils.py +++ b/node_cli/core/ssl/utils.py @@ -50,7 +50,7 @@ def detached_subprocess(cmd, expose_output=False): logger.debug(f'Starting detached subprocess: {cmd}') p = subprocess.Popen( cmd, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, encoding='utf-8' ) try: From fcadfd378d5bf804168929c5ae92c43db50cfac7 Mon Sep 17 00:00:00 2001 From: badrogger Date: Tue, 24 Jun 2025 17:56:01 +0100 Subject: [PATCH 4/5] Remove docker-compose requirement --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0e3c2b3..12741e27 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ SKALE Node CLI, part of the SKALE suite of validator tools, is the command line * Prerequisites -Ensure that the following package is installed: **docker**, **docker-compose** (1.27.4+) +Ensure that the following package is installed: **docker**, **docker-compose** * Download the executable From 3a0de05dd342318bd7c797a065d2bb504db67546 Mon Sep 17 00:00:00 2001 From: Dmytro Date: Tue, 16 Dec 2025 20:07:51 +0000 Subject: [PATCH 5/5] Update __init__.py --- node_cli/cli/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node_cli/cli/__init__.py b/node_cli/cli/__init__.py index e1e6add7..1fe20f2e 100644 --- a/node_cli/cli/__init__.py +++ b/node_cli/cli/__init__.py @@ -1,4 +1,4 @@ -__version__ = '2.6.2' +__version__ = '2.6.3' if __name__ == '__main__': print(__version__)