From 9d107b6954d5690731f86702105d2e29421f7226 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Fri, 17 Mar 2023 09:30:02 +0100 Subject: tests/http: add timeout to running curl in test cases - we had a CI case once where `curl` seemingly did not return and it was hard to guess what happened. - make curl execution in test cases time out after 60 seconds Closes #10783 --- tests/http/scorecard.py | 1 + tests/http/testenv/curl.py | 32 ++++++++++++++++++++++---------- tests/http/testenv/env.py | 9 +++++++++ tests/http/testenv/httpd.py | 3 ++- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 21dafe181..d9f789c9f 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -374,6 +374,7 @@ class ScoreCard: rv = 0 self.env = Env() self.env.setup() + self.env.test_timeout = None self.httpd = None self.nghttpx = None self.caddy = None diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index ec832eca0..13c4f8465 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -45,9 +45,11 @@ class ExecResult: def __init__(self, args: List[str], exit_code: int, stdout: List[str], stderr: List[str], duration: Optional[timedelta] = None, - with_stats: bool = False): + with_stats: bool = False, + exception: Optional[str] = None): self._args = args self._exit_code = exit_code + self._exception = exception self._stdout = stdout self._stderr = stderr self._duration = duration if duration is not None else timedelta() @@ -69,7 +71,8 @@ class ExecResult: pass def __repr__(self): - return f"ExecResult[code={self.exit_code}, args={self._args}, stdout={self._stdout}, stderr={self._stderr}]" + return f"ExecResult[code={self.exit_code}, exception={self._exception}, "\ + f"args={self._args}, stdout={self._stdout}, stderr={self._stderr}]" def _parse_stats(self): self._stats = [] @@ -78,7 +81,6 @@ class ExecResult: self._stats.append(json.loads(l)) except: log.error(f'not a JSON stat: {l}') - log.error(f'stdout is: {"".join(self._stdout)}') break @property @@ -197,8 +199,10 @@ class CurlClient: 'h3': '--http3-only', } - def __init__(self, env: Env, run_dir: Optional[str] = None): + def __init__(self, env: Env, run_dir: Optional[str] = None, + timeout: Optional[float] = None): self.env = env + self._timeout = timeout if timeout else env.test_timeout self._curl = os.environ['CURL'] if 'CURL' in os.environ else env.curl self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, 'curl') self._stdoutfile = f'{self._run_dir}/curl.stdout' @@ -320,14 +324,22 @@ class CurlClient: self._rmf(self._headerfile) self._rmf(self._tracefile) start = datetime.now() - with open(self._stdoutfile, 'w') as cout: - with open(self._stderrfile, 'w') as cerr: - p = subprocess.run(args, stderr=cerr, stdout=cout, - cwd=self._run_dir, shell=False, - input=intext.encode() if intext else None) + exception = None + try: + with open(self._stdoutfile, 'w') as cout: + with open(self._stderrfile, 'w') as cerr: + p = subprocess.run(args, stderr=cerr, stdout=cout, + cwd=self._run_dir, shell=False, + input=intext.encode() if intext else None, + timeout=self._timeout) + exitcode = p.returncode + except subprocess.TimeoutExpired as e: + log.warning(f'Timeout after {self._timeout}s: {args}') + exitcode = -1 + exception = 'TimeoutExpired' coutput = open(self._stdoutfile).readlines() cerrput = open(self._stderrfile).readlines() - return ExecResult(args=args, exit_code=p.returncode, + return ExecResult(args=args, exit_code=exitcode, exception=exception, stdout=coutput, stderr=cerrput, duration=datetime.now() - start, with_stats=with_stats) diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index bf08104d3..c34d79129 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -281,6 +281,7 @@ class Env: self._verbose = pytestconfig.option.verbose \ if pytestconfig is not None else 0 self._ca = None + self._test_timeout = 60.0 # seconds def issue_certs(self): if self._ca is None: @@ -305,6 +306,14 @@ class Env: def verbose(self) -> int: return self._verbose + @property + def test_timeout(self) -> Optional[float]: + return self._test_timeout + + @test_timeout.setter + def test_timeout(self, val: Optional[float]): + self._test_timeout = val + @property def gen_dir(self) -> str: return self.CONFIG.gen_dir diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index 4c7874581..5b20d31e2 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -171,7 +171,8 @@ class Httpd: return False def wait_live(self, timeout: timedelta): - curl = CurlClient(env=self.env, run_dir=self._tmp_dir) + curl = CurlClient(env=self.env, run_dir=self._tmp_dir, + timeout=timeout.total_seconds()) try_until = datetime.now() + timeout while datetime.now() < try_until: r = curl.http_get(url=f'http://{self.env.domain1}:{self.env.http_port}/') -- cgit v1.2.1