Skip to content

Commit 9cfdbc7

Browse files
committed
implement SimLN plugin via click
1 parent 9e5f37e commit 9cfdbc7

File tree

6 files changed

+163
-103
lines changed

6 files changed

+163
-103
lines changed

resources/plugins/simln/simln.py

Lines changed: 126 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
from time import sleep
77

88
import click
9-
10-
from warnet.k8s import download, get_pods_with_label, wait_for_pod
11-
from warnet.plugin import _get_plugin_directory as get_plugin_directory
9+
from kubernetes.stream import stream
10+
11+
from warnet.k8s import (
12+
download,
13+
get_default_namespace,
14+
get_pods_with_label,
15+
get_static_client,
16+
wait_for_pod,
17+
)
18+
from warnet.plugins import _get_plugins_directory as get_plugin_directory
1219
from warnet.process import run_command
1320
from warnet.status import _get_tank_status as network_status
1421

@@ -20,40 +27,62 @@
2027
console_handler.setFormatter(formatter)
2128
log.addHandler(console_handler)
2229

23-
lightning_selector = "mission=lightning"
30+
LIGHTNING_SELECTOR = "mission=lightning"
31+
32+
33+
@click.group()
34+
def simln():
35+
"""Commands for the SimLN plugin"""
36+
pass
37+
38+
39+
def warnet_register_plugin(register_command):
40+
register_command(simln)
2441

2542

2643
class SimLNError(Exception):
2744
pass
2845

2946

30-
def run_simln():
31-
"""Run a SimLN Plugin demo"""
32-
init_network()
33-
fund_wallets()
34-
wait_for_everyone_to_have_a_host()
47+
@simln.command()
48+
def run_demo():
49+
"""Run the SimLN Plugin demo"""
50+
_init_network()
51+
_fund_wallets()
52+
_wait_for_everyone_to_have_a_host()
3553
log.info(warnet("bitcoin rpc tank-0000 -generate 7"))
3654
# warnet("ln open-all-channels")
3755
manual_open_channels()
3856
log.info(warnet("bitcoin rpc tank-0000 -generate 7"))
3957
wait_for_gossip_sync(2)
4058
log.info("done waiting")
41-
pod_name = _prepare_and_launch_activity()
59+
pod_name = prepare_and_launch_activity()
4260
log.info(pod_name)
4361
wait_for_pod(pod_name, 60)
4462

4563

46-
def _prepare_and_launch_activity() -> str:
47-
sample_activity = get_example_activity()
64+
@simln.command()
65+
def list_simln_podnames():
66+
"""Get a list of simln pod names"""
67+
print([pod.metadata.name for pod in get_pods_with_label("mission=simln")])
68+
69+
70+
@simln.command()
71+
def download_results(pod_name: str):
72+
"""Download SimLN results to the current directory"""
73+
print(download(pod_name, source_path=Path("/working/results")))
74+
75+
76+
def prepare_and_launch_activity() -> str:
77+
sample_activity = _get_example_activity()
4878
log.info(f"Activity: {sample_activity}")
49-
pod_name = launch_activity(sample_activity)
79+
pod_name = _launch_activity(sample_activity)
5080
log.info("Sent command. Done.")
5181
return pod_name
5282

5383

54-
def get_example_activity() -> list[dict]:
55-
"""Get an activity representing node 2 sending msat to node 3"""
56-
pods = get_pods_with_label(lightning_selector)
84+
def _get_example_activity() -> list[dict]:
85+
pods = get_pods_with_label(LIGHTNING_SELECTOR)
5786
try:
5887
pod_a = pods[1].metadata.name
5988
pod_b = pods[2].metadata.name
@@ -64,7 +93,13 @@ def get_example_activity() -> list[dict]:
6493
return [{"source": pod_a, "destination": pod_b, "interval_secs": 1, "amount_msat": 2000}]
6594

6695

67-
def launch_activity(activity: list[dict]) -> str:
96+
@simln.command()
97+
def get_example_activity():
98+
"""Get an activity representing node 2 sending msat to node 3"""
99+
print(_get_example_activity())
100+
101+
102+
def _launch_activity(activity: list[dict]) -> str:
68103
"""Launch a SimLN chart which includes the `activity`"""
69104
random_digits = "".join(random.choices("0123456789", k=10))
70105
plugin_dir = get_plugin_directory()
@@ -75,7 +110,15 @@ def launch_activity(activity: list[dict]) -> str:
75110
return f"simln-simln-{random_digits}"
76111

77112

78-
def init_network():
113+
@simln.command()
114+
@click.argument("activity", type=str)
115+
def launch_activity(activity: str):
116+
"""Takes a SimLN Activity which is a JSON list of objects."""
117+
parsed_activity = json.loads(activity)
118+
print(_launch_activity(parsed_activity))
119+
120+
121+
def _init_network():
79122
"""Mine regtest coins and wait for ln nodes to come online."""
80123
log.info("Initializing network")
81124
wait_for_all_tanks_status(target="running")
@@ -85,7 +128,7 @@ def init_network():
85128
_wait_for_predicate(lambda: int(warnet("bitcoin rpc tank-0000 getblockcount")) > 100)
86129

87130
def wait_for_all_ln_rpc():
88-
lns = get_pods_with_label(lightning_selector)
131+
lns = get_pods_with_label(LIGHTNING_SELECTOR)
89132
for v1_pod in lns:
90133
ln = v1_pod.metadata.name
91134
try:
@@ -98,11 +141,16 @@ def wait_for_all_ln_rpc():
98141
_wait_for_predicate(wait_for_all_ln_rpc)
99142

100143

101-
def fund_wallets():
144+
@simln.command()
145+
def init_network():
146+
_init_network()
147+
148+
149+
def _fund_wallets():
102150
"""Fund each ln node with 10 regtest coins."""
103151
log.info("Funding wallets")
104152
outputs = ""
105-
lns = get_pods_with_label(lightning_selector)
153+
lns = get_pods_with_label(LIGHTNING_SELECTOR)
106154
for v1_pod in lns:
107155
lnd = v1_pod.metadata.name
108156
addr = json.loads(warnet(f"ln rpc {lnd} newaddress p2wkh"))["address"]
@@ -113,9 +161,15 @@ def fund_wallets():
113161
log.info(warnet("bitcoin rpc tank-0000 -generate 1"))
114162

115163

116-
def everyone_has_a_host() -> bool:
164+
@simln.command()
165+
def fund_wallets():
166+
"""Fund each ln node with 10 regtest coins."""
167+
_fund_wallets()
168+
169+
170+
def _everyone_has_a_host() -> bool:
117171
"""Find out if each ln node has a host."""
118-
pods = get_pods_with_label(lightning_selector)
172+
pods = get_pods_with_label(LIGHTNING_SELECTOR)
119173
host_havers = 0
120174
for pod in pods:
121175
name = pod.metadata.name
@@ -125,8 +179,13 @@ def everyone_has_a_host() -> bool:
125179
return host_havers == len(pods) and host_havers != 0
126180

127181

182+
@simln.command()
128183
def wait_for_everyone_to_have_a_host():
129-
_wait_for_predicate(everyone_has_a_host, timeout=10 * 60)
184+
log.info(_wait_for_everyone_to_have_a_host())
185+
186+
187+
def _wait_for_everyone_to_have_a_host():
188+
_wait_for_predicate(_everyone_has_a_host, timeout=10 * 60)
130189

131190

132191
def _wait_for_predicate(predicate, timeout=5 * 60, interval=5):
@@ -173,7 +232,7 @@ def wait_for_gossip_sync(expected: int = 2):
173232
current = 0
174233
while current < expected:
175234
current = 0
176-
pods = get_pods_with_label(lightning_selector)
235+
pods = get_pods_with_label(LIGHTNING_SELECTOR)
177236
for v1_pod in pods:
178237
node = v1_pod.metadata.name
179238
chs = json.loads(run_command(f"warnet ln rpc {node} describegraph"))["edges"]
@@ -196,7 +255,7 @@ def warnet(cmd: str = "--help"):
196255
def _generate_nodes_file(activity: list[dict], output_file: Path = Path("nodes.json")):
197256
nodes = []
198257

199-
for i in get_pods_with_label(lightning_selector):
258+
for i in get_pods_with_label(LIGHTNING_SELECTOR):
200259
name = i.metadata.name
201260
node = {
202261
"id": name,
@@ -252,28 +311,44 @@ def wait_for_two_txs():
252311
warnet("bitcoin rpc tank-0000 -generate 10")
253312

254313

255-
def list_simln_podnames() -> list[str]:
256-
"""Get a list of simln pod names"""
257-
return [pod.metadata.name for pod in get_pods_with_label("mission=simln")]
258-
259-
260-
def download_results(pod_name: str):
261-
"""Download SimLN results to the current directory"""
262-
download(pod_name, source_path=Path("/working/results"))
263-
264-
265-
@click.group()
266-
def pname():
267-
"""Commands for PluginName."""
268-
pass
269-
270-
271-
@pname.command()
272-
def pthing():
273-
"""Do another thing."""
274-
click.echo("Plugin is doing another thing!")
275-
run_simln()
276-
277-
278-
def _register(register_command):
279-
register_command(pname)
314+
def _rpc(pod, method: str, params: tuple[str, ...]) -> str:
315+
namespace = get_default_namespace()
316+
317+
sclient = get_static_client()
318+
if params:
319+
cmd = [method]
320+
cmd.extend(params)
321+
else:
322+
cmd = [method]
323+
resp = stream(
324+
sclient.connect_get_namespaced_pod_exec,
325+
pod,
326+
namespace,
327+
container="simln",
328+
command=cmd,
329+
stderr=True,
330+
stdin=False,
331+
stdout=True,
332+
tty=False,
333+
_preload_content=False,
334+
)
335+
stdout = ""
336+
stderr = ""
337+
while resp.is_open():
338+
resp.update(timeout=1)
339+
if resp.peek_stdout():
340+
stdout_chunk = resp.read_stdout()
341+
stdout += stdout_chunk
342+
if resp.peek_stderr():
343+
stderr_chunk = resp.read_stderr()
344+
stderr += stderr_chunk
345+
return stdout + stderr
346+
347+
348+
@simln.command(context_settings={"ignore_unknown_options": True})
349+
@click.argument("pod", type=str)
350+
@click.argument("method", type=str)
351+
@click.argument("params", type=str, nargs=-1) # this will capture all remaining arguments
352+
def rpc(pod: str, method: str, params: tuple[str, ...]):
353+
"""Run commands on a pod"""
354+
print(_rpc(pod, method, params))

src/warnet/constants.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@
4040

4141
# Plugin architecture
4242
PLUGINS_LABEL = "plugins"
43+
PLUGIN_YAML = "plugin.yaml"
4344
PLUGINS_DIR = RESOURCES_DIR.joinpath(PLUGINS_LABEL)
44-
HOOK_NAME_KEY = "hook_name" # this lives as a key in object.__annotations__
45-
HOOKS_API_STEM = "hooks_api"
46-
HOOKS_API_FILE = HOOKS_API_STEM + ".py"
4745
WARNET_USER_DIR_ENV_VAR = "WARNET_USER_DIR"
4846

4947
# Helm charts

src/warnet/k8s.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ def download(
569569
source_path: Path,
570570
destination_path: Path = Path("."),
571571
namespace: Optional[str] = None,
572-
):
572+
) -> Path:
573573
"""Download the item from the `source_path` to the `destination_path`"""
574574

575575
namespace = get_default_namespace_or(namespace)
@@ -607,3 +607,5 @@ def download(
607607
tar.extractall(path=destination_path)
608608

609609
os.remove(tar_file)
610+
611+
return destination_path

src/warnet/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from .graph import create, graph, import_network
99
from .image import image
1010
from .ln import ln
11-
from .plugin import load_plugins, plugin
11+
from .plugins import load_plugins, plugins
1212
from .project import init, new, setup
1313
from .status import status
1414
from .users import auth
@@ -38,7 +38,7 @@ def cli():
3838
cli.add_command(status)
3939
cli.add_command(stop)
4040
cli.add_command(create)
41-
cli.add_command(plugin)
41+
cli.add_command(plugins)
4242

4343

4444
@load_plugins

0 commit comments

Comments
 (0)