Skip to content

Commit 1c5c2ae

Browse files
committed
scenarios_test passing
1 parent 3877210 commit 1c5c2ae

File tree

6 files changed

+75
-98
lines changed

6 files changed

+75
-98
lines changed

resources/images/commander/src/commander.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,6 @@ def ensure_miner(node):
5252
node.createwallet("miner", descriptors=True)
5353
return node.get_wallet_rpc("miner")
5454

55-
def network_connected(self):
56-
for tank in self.nodes:
57-
peerinfo = tank.getpeerinfo()
58-
manuals = 0
59-
for peer in peerinfo:
60-
if peer["connection_type"] == "manual":
61-
manuals += 1
62-
# Even if more edges are specifed, bitcoind only allows
63-
# 8 manual outbound connections
64-
if min(8, len(tank.init_peers)) > manuals:
65-
return False
66-
return True
67-
6855
def handle_sigterm(self, signum, frame):
6956
print("SIGTERM received, stopping...")
7057
self.shutdown()

src/warnet/cli/k8s.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,8 @@ def apply_kubernetes_yaml(yaml_file: str):
9292
def delete_namespace(namespace: str):
9393
command = f"kubectl delete namespace {namespace}"
9494
return stream_command(command)
95+
96+
97+
def delete_pod(pod_name: str):
98+
command = f"kubectl delete pod {pod_name}"
99+
return stream_command(command)

src/warnet/cli/scenarios.py

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def available():
2828
"""
2929
console = Console()
3030

31-
scenario_list = []
31+
scenario_list = _available()
3232
for s in pkgutil.iter_modules(SCENARIOS.__path__):
3333
scenario_list.append(s.name)
3434

@@ -41,6 +41,13 @@ def available():
4141
console.print(table)
4242

4343

44+
def _available():
45+
scenario_list = []
46+
for s in pkgutil.iter_modules(SCENARIOS.__path__):
47+
scenario_list.append(s.name)
48+
return scenario_list
49+
50+
4451
@scenarios.command(context_settings={"ignore_unknown_options": True})
4552
@click.argument("scenario", type=str)
4653
@click.argument("additional_args", nargs=-1, type=click.UNPROCESSED)
@@ -56,14 +63,32 @@ def run(scenario, additional_args):
5663
# Ensure the scenario file exists within the package
5764
with importlib.resources.path(scenario_package, scenario_filename) as scenario_path:
5865
scenario_path = str(scenario_path) # Convert Path object to string
66+
return run_scenario(scenario_path, additional_args)
67+
68+
69+
@scenarios.command(context_settings={"ignore_unknown_options": True})
70+
@click.argument("scenario_path", type=str)
71+
@click.argument("additional_args", nargs=-1, type=click.UNPROCESSED)
72+
def run_file(scenario_path, additional_args):
73+
"""
74+
Run <scenario_path> from the Warnet Test Framework with optional arguments
75+
"""
76+
if not scenario_path.endswith(".py"):
77+
print("Error. Currently only python scenarios are supported")
78+
sys.exit(1)
79+
return run_scenario(scenario_path, additional_args)
80+
5981

82+
def run_scenario(scenario_path, additional_args):
6083
if not os.path.exists(scenario_path):
61-
raise Exception(f"Scenario {scenario} not found at {scenario_path}.")
84+
raise Exception(f"Scenario file not found at {scenario_path}.")
6285

6386
with open(scenario_path) as file:
6487
scenario_text = file.read()
6588

66-
name = f"commander-{scenario.replace('_', '')}-{int(time.time())}"
89+
scenario_name = os.path.splitext(os.path.basename(scenario_path))[0]
90+
91+
name = f"commander-{scenario_name.replace('_', '')}-{int(time.time())}"
6792

6893
tankpods = get_mission("tank")
6994
tanks = [
@@ -142,37 +167,12 @@ def run(scenario, additional_args):
142167
apply_kubernetes_yaml(temp_file_path)
143168

144169

145-
@scenarios.command(context_settings={"ignore_unknown_options": True})
146-
@click.argument("scenario_path", type=str)
147-
@click.argument("additional_args", nargs=-1, type=click.UNPROCESSED)
148-
@click.option("--name", type=str)
149-
def run_file(scenario_path, additional_args, name=""):
150-
"""
151-
Run <scenario_path> from the Warnet Test Framework with optional arguments
152-
"""
153-
if not scenario_path.endswith(".py"):
154-
print("Error. Currently only python scenarios are supported")
155-
sys.exit(1)
156-
scenario_name = name if name else os.path.splitext(os.path.basename(scenario_path))[0]
157-
scenario_base64 = ""
158-
with open(scenario_path, "rb") as f:
159-
scenario_base64 = base64.b64encode(f.read()).decode("utf-8")
160-
161-
params = {
162-
"scenario_base64": scenario_base64,
163-
"scenario_name": scenario_name,
164-
"additional_args": additional_args,
165-
}
166-
# TODO
167-
# print(rpc_call("scenarios_run_file", params))
168-
169-
170170
@scenarios.command()
171171
def active():
172172
"""
173173
List running scenarios "name": "pid" pairs
174174
"""
175-
commanders = get_mission("commander")
175+
commanders = _active()
176176
if len(commanders) == 0:
177177
print("No scenarios running")
178178
return
@@ -182,7 +182,12 @@ def active():
182182
table.add_column("Status")
183183

184184
for commander in commanders:
185-
table.add_row(commander.metadata.name, commander.status.phase)
185+
table.add_row(commander["commander"], commander["status"])
186186

187187
console = Console()
188188
console.print(table)
189+
190+
191+
def _active():
192+
commanders = get_mission("commander")
193+
return [{"commander": c.metadata.name, "status": c.status.phase.lower()} for c in commanders]

src/warnet/scenarios/miner_std.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@ def add_options(self, parser):
4646
)
4747

4848
def run_test(self):
49-
while not self.network_connected():
50-
self.log.info("Waiting for complete network connection...")
51-
sleep(5)
52-
self.log.info("Network connected. Starting miners.")
49+
self.log.info("Starting miners.")
5350

5451
max_miners = 1
5552
if self.options.allnodes:

test/data/scenario_p2p_interface.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
from test_framework.messages import CInv, msg_getdata
66
from test_framework.p2p import P2PInterface
7-
from warnet.test_framework_bridge import WarnetTestFramework
87

8+
# The base class exists inside the commander container
9+
from commander import Commander
910

1011
def cli_help():
1112
return "Run P2P GETDATA test"
@@ -21,20 +22,15 @@ def on_block(self, message):
2122
self.blocks[message.block.sha256] += 1
2223

2324

24-
class GetdataTest(WarnetTestFramework):
25+
class GetdataTest(Commander):
2526
def set_test_params(self):
2627
self.num_nodes = 1
2728

2829
def run_test(self):
29-
while not self.warnet.network_connected():
30-
self.log.info("Waiting for complete network connection...")
31-
sleep(5)
32-
self.log.info("Network connected")
33-
3430
self.log.info("Adding the p2p connection")
3531

3632
p2p_block_store = self.nodes[0].add_p2p_connection(
37-
P2PStoreBlock(), dstaddr=self.warnet.tanks[0].ipv4, dstport=18444
33+
P2PStoreBlock(), dstaddr=self.nodes[0].rpchost, dstport=18444
3834
)
3935

4036
self.log.info("test that an invalid GETDATA doesn't prevent processing of future messages")

test/scenarios_test.py

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import os
44
from pathlib import Path
5-
5+
from warnet.cli.scenarios import _available as scenarios_available
6+
from warnet.cli.scenarios import _active as scenarios_active
7+
from warnet.cli.k8s import delete_pod
68
from test_base import TestBase
79

810

@@ -13,7 +15,6 @@ def __init__(self):
1315

1416
def run_test(self):
1517
try:
16-
self.start_server()
1718
self.setup_network()
1819
self.test_scenarios()
1920
finally:
@@ -23,85 +24,71 @@ def setup_network(self):
2324
self.log.info("Setting up network")
2425
self.log.info(self.warcli(f"network start {self.graph_file_path}"))
2526
self.wait_for_all_tanks_status(target="running")
27+
self.wait_for_all_edges()
2628

2729
def test_scenarios(self):
2830
self.check_available_scenarios()
29-
self.run_and_check_miner_scenario("miner_std")
30-
self.run_and_check_miner_scenario_from_file("src/warnet/scenarios/miner_std.py")
31-
self.run_and_check_scenario_from_file("test/data/scenario_p2p_interface.py")
31+
self.run_and_check_miner_scenario()
32+
self.run_and_check_miner_scenario_from_file()
33+
self.run_and_check_scenario_from_file()
3234

3335
def check_available_scenarios(self):
3436
self.log.info("Checking available scenarios")
3537
# Use rpc instead of warcli so we get raw JSON object
36-
scenarios = self.rpc("scenarios_available")
38+
scenarios = scenarios_available()
3739
assert len(scenarios) == 4, f"Expected 4 available scenarios, got {len(scenarios)}"
3840
self.log.info(f"Found {len(scenarios)} available scenarios")
3941

4042
def scenario_running(self, scenario_name: str):
4143
"""Check that we are only running a single scenario of the correct name"""
42-
active = self.rpc("scenarios_list_running")
43-
running = scenario_name in active[0]["cmd"]
44-
return running and len(active) == 1
44+
active = scenarios_active()
45+
assert len(active) == 1
46+
return scenario_name in active[0]["commander"]
4547

46-
def run_and_check_scenario_from_file(self, scenario_file):
47-
scenario_name = self.get_scenario_name_from_path(scenario_file)
48+
def run_and_check_scenario_from_file(self):
49+
scenario_file = "test/data/scenario_p2p_interface.py"
4850

4951
def check_scenario_clean_exit():
50-
running = self.rpc("scenarios_list_running")
51-
scenarios = [s for s in running if s["cmd"].strip() == scenario_name]
52-
if not scenarios:
53-
return False
54-
scenario = scenarios[0]
55-
if scenario["active"]:
56-
return False
57-
if scenario["return_code"] != 0:
58-
raise Exception(
59-
f"Scenario {scenario_name} failed with return code {scenario['return_code']}"
60-
)
61-
return True
62-
63-
self.log.info(f"Running scenario: {scenario_name}")
52+
active = scenarios_active()
53+
assert len(active) == 1
54+
return active[0]["status"] == "succeeded"
55+
56+
self.log.info(f"Running scenario from: {scenario_file}")
6457
self.warcli(f"scenarios run-file {scenario_file}")
6558
self.wait_for_predicate(lambda: check_scenario_clean_exit())
6659

67-
def run_and_check_miner_scenario(self, scenario_name):
68-
self.log.info(f"Running scenario: {scenario_name}")
69-
self.warcli(f"scenarios run {scenario_name} --allnodes --interval=1")
70-
self.wait_for_predicate(lambda: self.scenario_running(scenario_name))
60+
def run_and_check_miner_scenario(self):
61+
sc = "miner_std"
62+
self.log.info(f"Running scenario {sc}")
63+
self.warcli(f"scenarios run {sc} --allnodes --interval=1")
64+
self.wait_for_predicate(lambda: self.scenario_running("commander-minerstd"))
7165
self.wait_for_predicate(lambda: self.check_blocks(30))
7266
self.stop_scenario()
7367

74-
def run_and_check_miner_scenario_from_file(self, scenario_file):
68+
def run_and_check_miner_scenario_from_file(self):
69+
scenario_file = "src/warnet/scenarios/miner_std.py"
7570
self.log.info(f"Running scenario from file: {scenario_file}")
7671
self.warcli(f"scenarios run-file {scenario_file} --allnodes --interval=1")
7772
start = int(self.warcli("bitcoin rpc 0 getblockcount"))
78-
scenario_name = self.get_scenario_name_from_path(scenario_file)
79-
self.wait_for_predicate(lambda: self.scenario_running(scenario_name))
73+
self.wait_for_predicate(lambda: self.scenario_running("commander-minerstd"))
8074
self.wait_for_predicate(lambda: self.check_blocks(2, start=start))
8175
self.stop_scenario()
8276

83-
def get_scenario_name_from_path(self, scenario_file):
84-
return os.path.splitext(os.path.basename(scenario_file))[0]
85-
8677
def check_blocks(self, target_blocks, start: int = 0):
87-
running = self.rpc("scenarios_list_running")
88-
assert len(running) == 1, f"Expected one running scenario, got {len(running)}"
89-
assert running[0]["active"], "Scenario should be active"
90-
9178
count = int(self.warcli("bitcoin rpc 0 getblockcount"))
9279
self.log.debug(f"Current block count: {count}, target: {start + target_blocks}")
9380
return count >= start + target_blocks
9481

9582
def stop_scenario(self):
9683
self.log.info("Stopping running scenario")
97-
running = self.rpc("scenarios_list_running")
84+
running = scenarios_active()
9885
assert len(running) == 1, f"Expected one running scenario, got {len(running)}"
99-
assert running[0]["active"], "Scenario should be active"
100-
self.warcli(f"scenarios stop {running[0]['pid']}", False)
86+
assert running[0]["status"] == "running", "Scenario should be running"
87+
delete_pod(running[0]["commander"])
10188
self.wait_for_predicate(self.check_scenario_stopped)
10289

10390
def check_scenario_stopped(self):
104-
running = self.rpc("scenarios_list_running")
91+
running = scenarios_active()
10592
self.log.debug(f"Checking if scenario stopped. Running scenarios: {len(running)}")
10693
return len(running) == 0
10794

0 commit comments

Comments
 (0)