66from time import sleep
77
88import 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
1219from warnet .process import run_command
1320from warnet .status import _get_tank_status as network_status
1421
2027console_handler .setFormatter (formatter )
2128log .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
2643class 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 ()
128183def 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
132191def _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"):
196255def _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 ))
0 commit comments