Skip to content

Commit 7530c3b

Browse files
committed
deploy plugins from network.yaml
1 parent f3b3362 commit 7530c3b

File tree

5 files changed

+92
-61
lines changed

5 files changed

+92
-61
lines changed

resources/plugins/simln/simln.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@ def get_example_activity():
105105
print(json.dumps(_get_example_activity()))
106106

107107

108+
# Take note of how click expects us to explicitly declare command line arguments.
109+
@simln.command()
110+
@click.argument("activity", type=str)
111+
@click.pass_context
112+
def launch_activity(ctx, activity: str):
113+
"""Deploys a SimLN Activity which is a JSON list of objects"""
114+
try:
115+
parsed_activity = json.loads(activity)
116+
except json.JSONDecodeError:
117+
log.error("Invalid JSON input for activity.")
118+
raise click.BadArgumentUsage("Activity must be a valid JSON string.") from None
119+
plugin_dir = ctx.obj.get(PLUGIN_DIR_TAG)
120+
print(_launch_activity(parsed_activity, plugin_dir))
121+
122+
108123
def _launch_activity(activity: list[dict], plugin_dir: str) -> str:
109124
"""Launch a SimLN chart which includes the `activity`"""
110125
timestamp = int(time.time())
@@ -128,21 +143,6 @@ def _launch_activity(activity: list[dict], plugin_dir: str) -> str:
128143
raise SimLNError(f"Could not write sim.json to the init container: {name}")
129144

130145

131-
# Take note of how click expects us to explicitly declare command line arguments.
132-
@simln.command()
133-
@click.argument("activity", type=str)
134-
@click.pass_context
135-
def launch_activity(ctx, activity: str):
136-
"""Deploys a SimLN Activity which is a JSON list of objects"""
137-
try:
138-
parsed_activity = json.loads(activity)
139-
except json.JSONDecodeError:
140-
log.error("Invalid JSON input for activity.")
141-
raise click.BadArgumentUsage("Activity must be a valid JSON string.") from None
142-
plugin_dir = ctx.obj.get(PLUGIN_DIR_TAG)
143-
print(_launch_activity(parsed_activity, plugin_dir))
144-
145-
146146
def _generate_activity_json(activity: list[dict]) -> str:
147147
nodes = []
148148

src/warnet/deploy.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
wait_for_ingress_controller,
3333
wait_for_pod_ready,
3434
)
35-
from .process import stream_command
35+
from .process import run_command, stream_command
3636

3737
HINT = "\nAre you trying to run a scenario? See `warnet run --help`"
3838

@@ -92,6 +92,19 @@ def _deploy(directory, debug, namespace, to_all_users):
9292
)
9393

9494

95+
def run_plugins(directory):
96+
network_file_path = directory / NETWORK_FILE
97+
98+
with network_file_path.open() as f:
99+
network_file = yaml.safe_load(f)
100+
101+
plugins = network_file.get("plugins") or []
102+
for plugin_cmd in plugins:
103+
fully_qualified_cmd = f"{network_file_path.parent}/{plugin_cmd}" # relative to network.yaml
104+
print(fully_qualified_cmd)
105+
print(run_command(fully_qualified_cmd))
106+
107+
95108
def check_logging_required(directory: Path):
96109
# check if node-defaults has logging or metrics enabled
97110
default_file_path = directory / DEFAULTS_FILE
@@ -106,7 +119,8 @@ def check_logging_required(directory: Path):
106119
network_file_path = directory / NETWORK_FILE
107120
with network_file_path.open() as f:
108121
network_file = yaml.safe_load(f)
109-
nodes = network_file.get("nodes", [])
122+
123+
nodes = network_file.get("nodes") or []
110124
for node in nodes:
111125
if node.get("collectLogs", False):
112126
return True
@@ -238,7 +252,8 @@ def deploy_network(directory: Path, debug: bool = False, namespace: Optional[str
238252

239253
needs_ln_init = False
240254

241-
for node in network_file["nodes"]:
255+
nodes = network_file.get("nodes") or []
256+
for node in nodes:
242257
click.echo(f"Deploying node: {node.get('name')}")
243258
try:
244259
temp_override_file_path = ""

test/data/ln/network.yaml

+4-1
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,7 @@ nodes:
5151
addnode:
5252
- tank-0000
5353
ln:
54-
lnd: true
54+
lnd: true
55+
56+
plugins:
57+
- "../../../resources/plugins/simln/simln.py launch-activity '[{\"source\": \"tank-0003-ln\", \"destination\": \"tank-0005-ln\", \"interval_secs\": 1, \"amount_msat\": 2000}]'"

test/ln_test.py

+35-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#!/usr/bin/env python3
2-
2+
import ast
33
import json
44
import os
55
from pathlib import Path
6+
from typing import Optional
67

78
from test_base import TestBase
89

10+
from warnet.k8s import wait_for_pod
911
from warnet.process import run_command, stream_command
1012

1113

@@ -16,6 +18,7 @@ def __init__(self):
1618
self.imported_network_dir = self.tmpdir / "imported_network"
1719
self.scen_dir = Path(os.path.dirname(__file__)).parent / "resources" / "scenarios"
1820
self.plugins_dir = Path(os.path.dirname(__file__)).parent / "resources" / "plugins"
21+
self.simln_exec = Path("simln/simln.py")
1922

2023
def run_test(self):
2124
try:
@@ -84,12 +87,37 @@ def get_and_pay(src, tgt):
8487
get_and_pay(4, 6)
8588

8689
def run_simln(self):
87-
self.log.info("Running activity")
88-
activity_cmd = f"{self.plugins_dir}/simln/simln.py get-example-activity"
89-
activity = run_command(activity_cmd).strip()
90-
self.log.info(f"Activity: {activity}")
91-
command = f"{self.plugins_dir}/simln/simln.py launch-activity '{activity}'"
92-
self.log.info(run_command(command))
90+
self.log.info("Running SimLN...")
91+
activity_cmd = f"{self.plugins_dir}/{self.simln_exec} get-example-activity"
92+
activity = run_command(activity_cmd)
93+
launch_cmd = f"{self.plugins_dir}/{self.simln_exec} launch-activity '{activity}'"
94+
pod_names_literal = run_command(launch_cmd)
95+
pods = ast.literal_eval(pod_names_literal)
96+
wait_for_pod(pods[0])
97+
self.log.info("Checking SimLN...")
98+
self.wait_for_predicate(self.found_results_remotely)
99+
self.log.info("SimLN was successful.")
100+
101+
def found_results_remotely(self, pod: Optional[str] = None) -> bool:
102+
if pod is None:
103+
pod = self.get_first_simln_pod()
104+
self.log.info(f"Checking for results file in {pod}")
105+
results_file = run_command(
106+
f"{self.plugins_dir}/{self.simln_exec} sh {pod} ls /working/results"
107+
).strip()
108+
self.log.info(f"Results file: {results_file}")
109+
results = run_command(
110+
f"{self.plugins_dir}/{self.simln_exec} sh {pod} cat /working/results/{results_file}"
111+
).strip()
112+
self.log.info(results)
113+
return results.find("Success") > 0
114+
115+
def get_first_simln_pod(self):
116+
command = f"{self.plugins_dir}/{self.simln_exec} list-pod-names"
117+
pod_names_literal = run_command(command)
118+
self.log.info(f"{command}: {pod_names_literal}")
119+
pod_names = ast.literal_eval(pod_names_literal)
120+
return pod_names[0]
93121

94122

95123
if __name__ == "__main__":

test/simln_test.py

+20-35
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@
88
from typing import Optional
99

1010
import pexpect
11-
from ln_test import LNTest
1211
from test_base import TestBase
1312

1413
from warnet.constants import LIGHTNING_MISSION
15-
from warnet.k8s import download, get_mission, pod_log, wait_for_pod
14+
from warnet.k8s import download, get_mission, wait_for_pod
1615
from warnet.process import run_command
1716

1817

19-
class SimLNTest(LNTest, TestBase):
18+
class SimLNTest(TestBase):
2019
def __init__(self):
2120
super().__init__()
2221
self.network_dir = Path(os.path.dirname(__file__)) / "data" / "ln"
@@ -27,15 +26,8 @@ def run_test(self):
2726
try:
2827
os.chdir(self.tmpdir)
2928
self.init_directory()
30-
31-
self.import_network()
32-
self.setup_network()
33-
self.test_channel_policies()
34-
self.test_payments()
35-
self.run_simln()
36-
29+
self.deploy_with_plugin()
3730
self.copy_results()
38-
self.run_activity()
3931
finally:
4032
self.cleanup()
4133

@@ -46,32 +38,20 @@ def init_directory(self):
4638
self.sut.sendline("n")
4739
self.sut.close()
4840

49-
def copy_results(self):
50-
self.log.info("Copying results")
51-
pod = get_mission(f"{self.simln_exec} mission")[0]
52-
self.wait_for_gossip_sync(2)
53-
wait_for_pod(pod.metadata.name, 60)
54-
55-
log_resp = pod_log(pod.metadata.name, f"{self.simln_exec} primary-container")
56-
self.log.info(log_resp.data.decode("utf-8"))
41+
def deploy_with_plugin(self):
42+
self.log.info("Deploy the ln network with a SimLN plugin")
43+
results = self.warnet(f"deploy {self.network_dir}")
44+
self.log.info(results)
45+
wait_for_pod(self.get_first_simln_pod())
5746

58-
partial_func = partial(self.found_results_remotely, pod.metadata.name)
47+
def copy_results(self):
48+
pod = self.get_first_simln_pod()
49+
partial_func = partial(self.found_results_remotely, pod)
5950
self.wait_for_predicate(partial_func)
6051

61-
download(pod.metadata.name, Path("/working/results"), Path("."), pod.metadata.namespace)
52+
download(pod, Path("/working/results"), Path("."))
6253
self.wait_for_predicate(self.found_results_locally)
6354

64-
def run_activity(self):
65-
cmd = f"{self.simln_exec} get-example-activity"
66-
self.log.info(f"Activity: {cmd}")
67-
activity_result = run_command(cmd)
68-
activity = json.loads(activity_result)
69-
pod_result = run_command(f"{self.simln_exec} launch-activity '{json.dumps(activity)}'")
70-
self.log.info(f"launched activity: {pod_result}")
71-
partial_func = partial(self.found_results_remotely, pod_result.strip())
72-
self.wait_for_predicate(partial_func)
73-
self.log.info("Successfully ran activity")
74-
7555
def wait_for_gossip_sync(self, expected: int):
7656
self.log.info(f"Waiting for sync (expecting {expected})...")
7757
current = 0
@@ -88,9 +68,7 @@ def wait_for_gossip_sync(self, expected: int):
8868

8969
def found_results_remotely(self, pod: Optional[str] = None) -> bool:
9070
if pod is None:
91-
pod_names_literal = run_command(f"{self.simln_exec} list-pod-names")
92-
pod_names = ast.literal_eval(pod_names_literal)
93-
pod = pod_names[0]
71+
pod = self.get_first_simln_pod()
9472
self.log.info(f"Checking for results file in {pod}")
9573
results_file = run_command(f"{self.simln_exec} sh {pod} ls /working/results").strip()
9674
self.log.info(f"Results file: {results_file}")
@@ -100,6 +78,13 @@ def found_results_remotely(self, pod: Optional[str] = None) -> bool:
10078
self.log.info(results)
10179
return results.find("Success") > 0
10280

81+
def get_first_simln_pod(self):
82+
command = f"{self.simln_exec} list-pod-names"
83+
pod_names_literal = run_command(command)
84+
self.log.info(f"{command}: {pod_names_literal}")
85+
pod_names = ast.literal_eval(pod_names_literal)
86+
return pod_names[0]
87+
10388
def found_results_locally(self) -> bool:
10489
directory = "results"
10590
self.log.info(f"Searching {directory}")

0 commit comments

Comments
 (0)