88import click
99from kubernetes .stream import stream
1010
11- # When we want to select pods based on their role in Warnet, we use "mission" tags.
11+ # When we want to select pods based on their role in Warnet, we use "mission" tags. The "mission"
12+ # tag for "lightning" nodes is stored in LIGHTNING_MISSION.
1213from warnet .constants import LIGHTNING_MISSION
1314from warnet .k8s import (
1415 download ,
2122from warnet .process import run_command
2223from warnet .status import _get_tank_status as network_status
2324
24- # To make a "mission" tag for your plugin, declare it here. This can be read by the warnet logging
25- # system. This should match the helm file associated with this plugin.
25+ # To make a "mission" tag for your plugin, declare it using the variable name MISSION. This will
26+ # be read by the warnet log system and status system.
27+ # This should match the pod's "mission" value in this plugin's associated helm file.
2628MISSION = "simln"
2729
2830# Each pod we deploy should have a primary container. We make the name of that primary container
29- # explicit here. This should match the helm file associated with this plugin.
31+ # explicit here using the variable name CONTAINER which Warnet uses internally in its log and status
32+ # systems.
33+ # Again, this should match the container name provided in the associated helm file.
3034CONTAINER = MISSION
3135
3236
@@ -43,16 +47,22 @@ class SimLNError(Exception):
4347log .addHandler (console_handler )
4448
4549
50+ # Warnet uses a python package called "click" to manage terminal interactions with the user.
51+ # Each plugin must declare a click "group" by decorating a function named after the plugin.
52+ # This makes your plugin available in the plugin section of Warnet.
4653@click .group ()
4754def simln ():
4855 """Commands for the SimLN plugin"""
4956 pass
5057
5158
59+ # Make sure to register your plugin by adding the group function like so:
5260def warnet_register_plugin (register_command ):
53- register_command (simln )
61+ register_command (simln ) # <-- We added the group function here.
5462
5563
64+ # The group function name is then used in decorators to create commands. These commands are
65+ # available to users when the access your plugin from the command line in Warnet.
5666@simln .command ()
5767def run_demo ():
5868 """Run the SimLN Plugin demo"""
@@ -72,7 +82,7 @@ def run_demo():
7282@simln .command ()
7383def list_simln_podnames ():
7484 """Get a list of simln pod names"""
75- print ([pod .metadata .name for pod in get_mission ("simln" )])
85+ print ([pod .metadata .name for pod in get_mission (MISSION )])
7686
7787
7888@simln .command ()
@@ -89,6 +99,11 @@ def prepare_and_launch_activity() -> str:
8999 return pod_name
90100
91101
102+ # When we want to use a command inside our plugin and also provide that command to the user, we like
103+ # to create a private function whose name starts with an underscore. We also make a public function
104+ # with the same name except that we leave off the underscore, decorate it with the command
105+ # decorator, and also provide an instructive doc string which Warnet will display in the help
106+ # section of the command line program.
92107def _get_example_activity () -> list [dict ]:
93108 pods = get_mission (LIGHTNING_MISSION )
94109 try :
@@ -101,6 +116,7 @@ def _get_example_activity() -> list[dict]:
101116 return [{"source" : pod_a , "destination" : pod_b , "interval_secs" : 1 , "amount_msat" : 2000 }]
102117
103118
119+ # Notice how the command that we make available to the user simply calls our internal command.
104120@simln .command ()
105121def get_example_activity ():
106122 """Get an activity representing node 2 sending msat to node 3"""
@@ -118,6 +134,7 @@ def _launch_activity(activity: list[dict]) -> str:
118134 return f"simln-simln-{ random_digits } "
119135
120136
137+ # Take note of how click expects us to explicitly declare command line arguments.
121138@simln .command ()
122139@click .argument ("activity" , type = str )
123140def launch_activity (activity : str ):
@@ -151,6 +168,7 @@ def wait_for_all_ln_rpc():
151168
152169@simln .command ()
153170def init_network ():
171+ """Initialize the demo network."""
154172 _init_network ()
155173
156174
@@ -328,29 +346,32 @@ def _rpc(pod, method: str, params: tuple[str, ...]) -> str:
328346 cmd .extend (params )
329347 else :
330348 cmd = [method ]
331- resp = stream (
332- sclient .connect_get_namespaced_pod_exec ,
333- pod ,
334- namespace ,
335- container = "simln" ,
336- command = cmd ,
337- stderr = True ,
338- stdin = False ,
339- stdout = True ,
340- tty = False ,
341- _preload_content = False ,
342- )
343- stdout = ""
344- stderr = ""
345- while resp .is_open ():
346- resp .update (timeout = 1 )
347- if resp .peek_stdout ():
348- stdout_chunk = resp .read_stdout ()
349- stdout += stdout_chunk
350- if resp .peek_stderr ():
351- stderr_chunk = resp .read_stderr ()
352- stderr += stderr_chunk
353- return stdout + stderr
349+ try :
350+ resp = stream (
351+ sclient .connect_get_namespaced_pod_exec ,
352+ pod ,
353+ namespace ,
354+ container = "simln" ,
355+ command = cmd ,
356+ stderr = True ,
357+ stdin = False ,
358+ stdout = True ,
359+ tty = False ,
360+ _preload_content = False ,
361+ )
362+ stdout = ""
363+ stderr = ""
364+ while resp .is_open ():
365+ resp .update (timeout = 1 )
366+ if resp .peek_stdout ():
367+ stdout_chunk = resp .read_stdout ()
368+ stdout += stdout_chunk
369+ if resp .peek_stderr ():
370+ stderr_chunk = resp .read_stderr ()
371+ stderr += stderr_chunk
372+ return stdout + stderr
373+ except Exception as err :
374+ print (f"Could not execute stream: { err } " )
354375
355376
356377@simln .command (context_settings = {"ignore_unknown_options" : True })
0 commit comments