Skip to content

Commit dc5da7f

Browse files
author
MarcoFalke
committed
Merge #18691: test: add wait_for_cookie_credentials() to framework for rpcwait tests
92fe537 test: fix intermittent race condition in interface_bitcoin_cli.py (Jon Atack) c648e63 test: add wait_for_cookie_credentials() to test framework (Jon Atack) Pull request description: This PR adds a `wait_for_cookie_credentials()` method to the test framework and calls it before the `-rpcwait` tests, to avoid an intermittent race condition on the CI run with Valgrind where the cookie file isn't written yet when the CLI call with `-rpcwait` arrives to `get_auth_cookie()`. To reproduce/test, build with ```diff diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 60c4d06..3dd06c4758 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -291,6 +291,7 @@ static bool InitRPCAuthentication() bool StartHTTPRPC() { LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); + UninterruptibleSleep(std::chrono::seconds{11}); if (!InitRPCAuthentication()) ``` then run the test normally and with valgrind ``` test/functional/interface_bitcoin_cli.py -l debug valgrind test/functional/interface_bitcoin_cli.py -l debug ``` Thanks to Marco Falke for all the help. Closes #18684. Top commit has no ACKs. Tree-SHA512: 1b76635b5b1d6b05138affef7ab788aa3bc3fc75b0c69ba778ecdf81063cfe02a8dd7667cfd63a6c6e19b2dac47d7a8b755e334d8af5c0ab9d4026808ee96c83
2 parents f8102d9 + 92fe537 commit dc5da7f

File tree

2 files changed

+29
-17
lines changed

2 files changed

+29
-17
lines changed

test/functional/interface_bitcoin_cli.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
BALANCE = (BLOCKS - 100) * 50
1414

1515
class TestBitcoinCli(BitcoinTestFramework):
16-
1716
def set_test_params(self):
1817
self.setup_clean_chain = True
1918
self.num_nodes = 1
@@ -33,12 +32,12 @@ def run_test(self):
3332
user, password = get_auth_cookie(self.nodes[0].datadir, self.chain)
3433

3534
self.log.info("Test -stdinrpcpass option")
36-
assert_equal(BLOCKS, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount())
37-
assert_raises_process_error(1, "Incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input="foo").echo)
35+
assert_equal(BLOCKS, self.nodes[0].cli('-rpcuser={}'.format(user), '-stdinrpcpass', input=password).getblockcount())
36+
assert_raises_process_error(1, 'Incorrect rpcuser or rpcpassword', self.nodes[0].cli('-rpcuser={}'.format(user), '-stdinrpcpass', input='foo').echo)
3837

3938
self.log.info("Test -stdin and -stdinrpcpass")
40-
assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo())
41-
assert_raises_process_error(1, "Incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo)
39+
assert_equal(['foo', 'bar'], self.nodes[0].cli('-rpcuser={}'.format(user), '-stdin', '-stdinrpcpass', input=password + '\nfoo\nbar').echo())
40+
assert_raises_process_error(1, 'Incorrect rpcuser or rpcpassword', self.nodes[0].cli('-rpcuser={}'.format(user), '-stdin', '-stdinrpcpass', input='foo').echo)
4241

4342
self.log.info("Test connecting to a non-existing server")
4443
assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('-rpcport=1').echo)
@@ -52,7 +51,7 @@ def run_test(self):
5251
self.log.info("Test -getinfo returns expected network and blockchain info")
5352
if self.is_wallet_compiled():
5453
self.nodes[0].encryptwallet(password)
55-
cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
54+
cli_get_info = self.nodes[0].cli().send_cli('-getinfo')
5655
network_info = self.nodes[0].getnetworkinfo()
5756
blockchain_info = self.nodes[0].getblockchaininfo()
5857
assert_equal(cli_get_info['version'], network_info['version'])
@@ -76,20 +75,17 @@ def run_test(self):
7675
else:
7776
self.log.info("*** Wallet not compiled; cli getwalletinfo and -getinfo wallet tests skipped")
7877

79-
self.stop_node(0)
80-
8178
self.log.info("Test -version with node stopped")
82-
cli_response = self.nodes[0].cli("-version").send_cli()
79+
self.stop_node(0)
80+
cli_response = self.nodes[0].cli().send_cli('-version')
8381
assert "{} RPC client version".format(self.config['environment']['PACKAGE_NAME']) in cli_response
8482

85-
self.log.info("Test -rpcwait option waits for RPC connection instead of failing")
86-
# Start node without RPC connection.
87-
self.nodes[0].start()
88-
# Verify failure without -rpcwait.
89-
assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('getblockcount').echo)
90-
# Verify success using -rpcwait.
91-
assert_equal(BLOCKS, self.nodes[0].cli('-rpcwait', 'getblockcount').send_cli())
83+
self.log.info("Test -rpcwait option successfully waits for RPC connection")
84+
self.nodes[0].start() # start node without RPC connection
85+
self.nodes[0].wait_for_cookie_credentials() # ensure cookie file is available to avoid race condition
86+
blocks = self.nodes[0].cli('-rpcwait').send_cli('getblockcount')
9287
self.nodes[0].wait_for_rpc_connection()
88+
assert_equal(blocks, BLOCKS)
9389

9490

9591
if __name__ == '__main__':

test/functional/test_framework/test_node.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
MAX_NODES,
2727
append_config,
2828
delete_cookie_file,
29+
get_auth_cookie,
2930
get_rpc_proxy,
3031
rpc_url,
3132
wait_until,
@@ -237,12 +238,27 @@ def wait_for_rpc_connection(self):
237238
except OSError as e:
238239
if e.errno != errno.ECONNREFUSED: # Port not yet open?
239240
raise # unknown OS error
240-
except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
241+
except ValueError as e: # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
241242
if "No RPC credentials" not in str(e):
242243
raise
243244
time.sleep(1.0 / poll_per_s)
244245
self._raise_assertion_error("Unable to connect to bitcoind after {}s".format(self.rpc_timeout))
245246

247+
def wait_for_cookie_credentials(self):
248+
"""Ensures auth cookie credentials can be read, e.g. for testing CLI with -rpcwait before RPC connection is up."""
249+
self.log.debug("Waiting for cookie credentials")
250+
# Poll at a rate of four times per second.
251+
poll_per_s = 4
252+
for _ in range(poll_per_s * self.rpc_timeout):
253+
try:
254+
get_auth_cookie(self.datadir, self.chain)
255+
self.log.debug("Cookie credentials successfully retrieved")
256+
return
257+
except ValueError: # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
258+
pass # so we continue polling until RPC credentials are retrieved
259+
time.sleep(1.0 / poll_per_s)
260+
self._raise_assertion_error("Unable to retrieve cookie credentials after {}s".format(self.rpc_timeout))
261+
246262
def generate(self, nblocks, maxtries=1000000):
247263
self.log.debug("TestNode.generate() dispatches `generate` call to `generatetoaddress`")
248264
return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries)

0 commit comments

Comments
 (0)