Skip to content

Commit 82306a8

Browse files
authored
Merge pull request #649 from willcl-ark/speedy-startup
speedy startup
2 parents 0287fdf + 09b7230 commit 82306a8

File tree

3 files changed

+156
-66
lines changed

3 files changed

+156
-66
lines changed

src/warnet/constants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@
9696
},
9797
}
9898

99+
LOGGING_CRD_COMMANDS = [
100+
"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts",
101+
"helm repo update",
102+
"helm upgrade --install prometheus-operator-crds prometheus-community/prometheus-operator-crds",
103+
]
104+
99105
# Helm commands for logging setup
100106
# TODO: also lots of hardcode stuff in these helm commands, will need to fix this when moving to helm charts
101107
LOGGING_HELM_COMMANDS = [

src/warnet/control.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import time
77
import zipapp
88
from concurrent.futures import ThreadPoolExecutor, as_completed
9+
from multiprocessing import Pool
910
from pathlib import Path
1011
from typing import Optional
1112

@@ -112,10 +113,18 @@ def stop_scenario(scenario_name):
112113

113114

114115
def stop_all_scenarios(scenarios):
115-
"""Stop all active scenarios using Helm"""
116-
with console.status("[bold yellow]Stopping all scenarios...[/bold yellow]"):
117-
for scenario in scenarios:
118-
stop_scenario(scenario)
116+
"""Stop all active scenarios in parallel using multiprocessing"""
117+
118+
def stop_single(scenario):
119+
stop_scenario(scenario)
120+
return f"Stopped scenario: {scenario}"
121+
122+
with console.status("[bold yellow]Stopping all scenarios...[/bold yellow]"), Pool() as pool:
123+
results = pool.map(stop_single, scenarios)
124+
125+
for result in results:
126+
console.print(f"[bold green]{result}[/bold green]")
127+
119128
console.print("[bold green]All scenarios have been stopped.[/bold green]")
120129

121130

src/warnet/deploy.py

Lines changed: 137 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import subprocess
22
import sys
33
import tempfile
4+
from multiprocessing import Process
45
from pathlib import Path
56
from typing import Optional
67

@@ -15,6 +16,7 @@
1516
FORK_OBSERVER_CHART,
1617
HELM_COMMAND,
1718
INGRESS_HELM_COMMANDS,
19+
LOGGING_CRD_COMMANDS,
1820
LOGGING_HELM_COMMANDS,
1921
LOGGING_NAMESPACE,
2022
NAMESPACES_CHART_LOCATION,
@@ -75,17 +77,47 @@ def _deploy(directory, debug, namespace, to_all_users):
7577

7678
if to_all_users:
7779
namespaces = get_namespaces_by_type(WARGAMES_NAMESPACE_PREFIX)
80+
processes = []
7881
for namespace in namespaces:
79-
deploy(directory, debug, namespace.metadata.name, False)
82+
p = Process(target=deploy, args=(directory, debug, namespace.metadata.name, False))
83+
p.start()
84+
processes.append(p)
85+
for p in processes:
86+
p.join()
8087
return
8188

8289
if (directory / NETWORK_FILE).exists():
83-
dl = deploy_logging_stack(directory, debug)
84-
deploy_network(directory, debug, namespace=namespace)
85-
df = deploy_fork_observer(directory, debug)
86-
if dl | df:
87-
deploy_ingress(debug)
88-
deploy_caddy(directory, debug)
90+
processes = []
91+
# Deploy logging CRD first to avoid synchronisation issues
92+
deploy_logging_crd(directory, debug)
93+
94+
logging_process = Process(target=deploy_logging_stack, args=(directory, debug))
95+
logging_process.start()
96+
processes.append(logging_process)
97+
98+
network_process = Process(target=deploy_network, args=(directory, debug, namespace))
99+
network_process.start()
100+
101+
ingress_process = Process(target=deploy_ingress, args=(directory, debug))
102+
ingress_process.start()
103+
processes.append(ingress_process)
104+
105+
caddy_process = Process(target=deploy_caddy, args=(directory, debug))
106+
caddy_process.start()
107+
processes.append(caddy_process)
108+
109+
# Wait for the network process to complete
110+
network_process.join()
111+
112+
# Start the fork observer process immediately after network process completes
113+
fork_observer_process = Process(target=deploy_fork_observer, args=(directory, debug))
114+
fork_observer_process.start()
115+
processes.append(fork_observer_process)
116+
117+
# Wait for all other processes to complete
118+
for p in processes:
119+
p.join()
120+
89121
elif (directory / NAMESPACES_FILE).exists():
90122
deploy_namespaces(directory)
91123
else:
@@ -118,11 +150,30 @@ def check_logging_required(directory: Path):
118150
return False
119151

120152

153+
def deploy_logging_crd(directory: Path, debug: bool) -> bool:
154+
"""
155+
This function exists so we can parallelise the rest of the loggin stack
156+
installation
157+
"""
158+
if not check_logging_required(directory):
159+
return False
160+
161+
click.echo(
162+
"Found collectLogs or metricsExport in network definition, Deploying logging stack CRD"
163+
)
164+
165+
for command in LOGGING_CRD_COMMANDS:
166+
if not stream_command(command):
167+
print(f"Failed to run Helm command: {command}")
168+
return False
169+
return True
170+
171+
121172
def deploy_logging_stack(directory: Path, debug: bool) -> bool:
122173
if not check_logging_required(directory):
123174
return False
124175

125-
click.echo("Found collectLogs or metricsExport in network definition, Deploying logging stack")
176+
click.echo("Deploying logging stack")
126177

127178
for command in LOGGING_HELM_COMMANDS:
128179
if not stream_command(command):
@@ -144,7 +195,7 @@ def deploy_caddy(directory: Path, debug: bool):
144195
if not network_file.get(name, {}).get("enabled", False):
145196
return
146197

147-
cmd = f"{HELM_COMMAND} {name} {CADDY_CHART} --namespace {namespace}"
198+
cmd = f"{HELM_COMMAND} {name} {CADDY_CHART} --namespace {namespace} --create-namespace"
148199
if debug:
149200
cmd += " --debug"
150201

@@ -156,7 +207,15 @@ def deploy_caddy(directory: Path, debug: bool):
156207
click.echo("\nTo access the warnet dashboard run:\n warnet dashboard")
157208

158209

159-
def deploy_ingress(debug: bool):
210+
def deploy_ingress(directory: Path, debug: bool):
211+
# Deploy ingress if either logging or fork observer is enabled
212+
network_file_path = directory / NETWORK_FILE
213+
with network_file_path.open() as f:
214+
network_file = yaml.safe_load(f)
215+
fo_enabled = network_file.get("fork_observer", {}).get("enabled", False)
216+
logging_enabled = check_logging_required(directory)
217+
if not (fo_enabled or logging_enabled):
218+
return
160219
click.echo("Deploying ingress controller")
161220

162221
for command in INGRESS_HELM_COMMANDS:
@@ -231,41 +290,49 @@ def deploy_fork_observer(directory: Path, debug: bool) -> bool:
231290

232291
def deploy_network(directory: Path, debug: bool = False, namespace: Optional[str] = None):
233292
network_file_path = directory / NETWORK_FILE
234-
defaults_file_path = directory / DEFAULTS_FILE
235-
236293
namespace = get_default_namespace_or(namespace)
237294

238295
with network_file_path.open() as f:
239296
network_file = yaml.safe_load(f)
240297

298+
processes = []
241299
for node in network_file["nodes"]:
242-
click.echo(f"Deploying node: {node.get('name')}")
243-
try:
244-
temp_override_file_path = ""
245-
node_name = node.get("name")
246-
node_config_override = {k: v for k, v in node.items() if k != "name"}
247-
248-
cmd = f"{HELM_COMMAND} {node_name} {BITCOIN_CHART_LOCATION} --namespace {namespace} -f {defaults_file_path}"
249-
if debug:
250-
cmd += " --debug"
251-
252-
if node_config_override:
253-
with tempfile.NamedTemporaryFile(
254-
mode="w", suffix=".yaml", delete=False
255-
) as temp_file:
256-
yaml.dump(node_config_override, temp_file)
257-
temp_override_file_path = Path(temp_file.name)
258-
cmd = f"{cmd} -f {temp_override_file_path}"
259-
260-
if not stream_command(cmd):
261-
click.echo(f"Failed to run Helm command: {cmd}")
262-
return
263-
except Exception as e:
264-
click.echo(f"Error: {e}")
300+
p = Process(target=deploy_single_node, args=(node, directory, debug, namespace))
301+
p.start()
302+
processes.append(p)
303+
304+
for p in processes:
305+
p.join()
306+
307+
308+
def deploy_single_node(node, directory: Path, debug: bool, namespace: str):
309+
defaults_file_path = directory / DEFAULTS_FILE
310+
click.echo(f"Deploying node: {node.get('name')}")
311+
temp_override_file_path = ""
312+
try:
313+
node_name = node.get("name")
314+
node_config_override = {k: v for k, v in node.items() if k != "name"}
315+
316+
defaults_file_path = directory / DEFAULTS_FILE
317+
cmd = f"{HELM_COMMAND} {node_name} {BITCOIN_CHART_LOCATION} --namespace {namespace} -f {defaults_file_path}"
318+
if debug:
319+
cmd += " --debug"
320+
321+
if node_config_override:
322+
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as temp_file:
323+
yaml.dump(node_config_override, temp_file)
324+
temp_override_file_path = Path(temp_file.name)
325+
cmd = f"{cmd} -f {temp_override_file_path}"
326+
327+
if not stream_command(cmd):
328+
click.echo(f"Failed to run Helm command: {cmd}")
265329
return
266-
finally:
267-
if temp_override_file_path:
268-
Path(temp_override_file_path).unlink()
330+
except Exception as e:
331+
click.echo(f"Error: {e}")
332+
return
333+
finally:
334+
if temp_override_file_path:
335+
Path(temp_override_file_path).unlink()
269336

270337

271338
def deploy_namespaces(directory: Path):
@@ -284,32 +351,40 @@ def deploy_namespaces(directory: Path):
284351
)
285352
return
286353

354+
processes = []
287355
for namespace in namespaces_file["namespaces"]:
288-
click.echo(f"Deploying namespace: {namespace.get('name')}")
289-
try:
290-
temp_override_file_path = ""
291-
namespace_name = namespace.get("name")
292-
namespace_config_override = {k: v for k, v in namespace.items() if k != "name"}
293-
294-
cmd = f"{HELM_COMMAND} {namespace_name} {NAMESPACES_CHART_LOCATION} -f {defaults_file_path}"
295-
296-
if namespace_config_override:
297-
with tempfile.NamedTemporaryFile(
298-
mode="w", suffix=".yaml", delete=False
299-
) as temp_file:
300-
yaml.dump(namespace_config_override, temp_file)
301-
temp_override_file_path = Path(temp_file.name)
302-
cmd = f"{cmd} -f {temp_override_file_path}"
303-
304-
if not stream_command(cmd):
305-
click.echo(f"Failed to run Helm command: {cmd}")
306-
return
307-
except Exception as e:
308-
click.echo(f"Error: {e}")
356+
p = Process(target=deploy_single_namespace, args=(namespace, defaults_file_path))
357+
p.start()
358+
processes.append(p)
359+
360+
for p in processes:
361+
p.join()
362+
363+
364+
def deploy_single_namespace(namespace, defaults_file_path: Path):
365+
click.echo(f"Deploying namespace: {namespace.get('name')}")
366+
temp_override_file_path = ""
367+
try:
368+
namespace_name = namespace.get("name")
369+
namespace_config_override = {k: v for k, v in namespace.items() if k != "name"}
370+
371+
cmd = f"{HELM_COMMAND} {namespace_name} {NAMESPACES_CHART_LOCATION} -f {defaults_file_path}"
372+
373+
if namespace_config_override:
374+
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as temp_file:
375+
yaml.dump(namespace_config_override, temp_file)
376+
temp_override_file_path = Path(temp_file.name)
377+
cmd = f"{cmd} -f {temp_override_file_path}"
378+
379+
if not stream_command(cmd):
380+
click.echo(f"Failed to run Helm command: {cmd}")
309381
return
310-
finally:
311-
if temp_override_file_path:
312-
temp_override_file_path.unlink()
382+
except Exception as e:
383+
click.echo(f"Error: {e}")
384+
return
385+
finally:
386+
if temp_override_file_path:
387+
Path(temp_override_file_path).unlink()
313388

314389

315390
def is_windows():

0 commit comments

Comments
 (0)