8
8
import click
9
9
from kubernetes .stream import stream
10
10
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.
12
13
from warnet .constants import LIGHTNING_MISSION
13
14
from warnet .k8s import (
14
15
download ,
21
22
from warnet .process import run_command
22
23
from warnet .status import _get_tank_status as network_status
23
24
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.
26
28
MISSION = "simln"
27
29
28
30
# 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.
30
34
CONTAINER = MISSION
31
35
32
36
@@ -43,16 +47,22 @@ class SimLNError(Exception):
43
47
log .addHandler (console_handler )
44
48
45
49
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.
46
53
@click .group ()
47
54
def simln ():
48
55
"""Commands for the SimLN plugin"""
49
56
pass
50
57
51
58
59
+ # Make sure to register your plugin by adding the group function like so:
52
60
def warnet_register_plugin (register_command ):
53
- register_command (simln )
61
+ register_command (simln ) # <-- We added the group function here.
54
62
55
63
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.
56
66
@simln .command ()
57
67
def run_demo ():
58
68
"""Run the SimLN Plugin demo"""
@@ -72,7 +82,7 @@ def run_demo():
72
82
@simln .command ()
73
83
def list_simln_podnames ():
74
84
"""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 )])
76
86
77
87
78
88
@simln .command ()
@@ -89,6 +99,11 @@ def prepare_and_launch_activity() -> str:
89
99
return pod_name
90
100
91
101
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.
92
107
def _get_example_activity () -> list [dict ]:
93
108
pods = get_mission (LIGHTNING_MISSION )
94
109
try :
@@ -101,6 +116,7 @@ def _get_example_activity() -> list[dict]:
101
116
return [{"source" : pod_a , "destination" : pod_b , "interval_secs" : 1 , "amount_msat" : 2000 }]
102
117
103
118
119
+ # Notice how the command that we make available to the user simply calls our internal command.
104
120
@simln .command ()
105
121
def get_example_activity ():
106
122
"""Get an activity representing node 2 sending msat to node 3"""
@@ -118,6 +134,7 @@ def _launch_activity(activity: list[dict]) -> str:
118
134
return f"simln-simln-{ random_digits } "
119
135
120
136
137
+ # Take note of how click expects us to explicitly declare command line arguments.
121
138
@simln .command ()
122
139
@click .argument ("activity" , type = str )
123
140
def launch_activity (activity : str ):
@@ -151,6 +168,7 @@ def wait_for_all_ln_rpc():
151
168
152
169
@simln .command ()
153
170
def init_network ():
171
+ """Initialize the demo network."""
154
172
_init_network ()
155
173
156
174
@@ -328,29 +346,32 @@ def _rpc(pod, method: str, params: tuple[str, ...]) -> str:
328
346
cmd .extend (params )
329
347
else :
330
348
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 } " )
354
375
355
376
356
377
@simln .command (context_settings = {"ignore_unknown_options" : True })
0 commit comments