33import logging
44from enum import Enum
55from pathlib import Path
6+ import subprocess
67import time
78from typing import Optional
89
2526
2627PLUGIN_DIR_TAG = "plugin_dir"
2728
28-
2929class PluginError (Exception ):
3030 pass
3131
32-
3332log = logging .getLogger (MISSION )
3433if not log .hasHandlers ():
3534 console_handler = logging .StreamHandler ()
@@ -41,9 +40,9 @@ class PluginError(Exception):
4140log .propagate = True
4241
4342class PluginContent (Enum ):
44- MODE = "mode "
45- MAX_PENDING_HTLCS = "maxPendingHtlcs "
46- RATE_LIMIT = "rateLimit "
43+ POD_NAME = "podName "
44+ LND_RPC_SERVER = "rpcserver "
45+ HTTP_LISTEN = "httplisten "
4746
4847@click .group ()
4948@click .pass_context
@@ -85,48 +84,121 @@ def _entrypoint(ctx, plugin_content: dict, warnet_content: dict):
8584 hook_value = warnet_content [WarnetContent .HOOK_VALUE .value ]
8685
8786 match hook_value :
88- case (
89- HookValue .PRE_NETWORK
90- | HookValue .POST_NETWORK
91- | HookValue .PRE_DEPLOY
92- | HookValue .POST_DEPLOY
93- ):
87+ case HookValue .POST_DEPLOY :
9488 data = get_data (plugin_content )
9589 if data :
96- _launch_circuit_breaker (ctx , node_name = hook_value . value . lower () + "breaker" , hook_value = hook_value . value )
90+ _launch_pod (ctx , install_name = "circuitbreaker" , ** data )
9791 else :
98- _launch_circuit_breaker (ctx , node_name = hook_value .value .lower ()+ "breaker" ,hook_value = hook_value .value )
99- case HookValue .PRE_NODE :
100- name = warnet_content [PLUGIN_ANNEX ][AnnexMember .NODE_NAME .value ] + "-pre-pod"
101- _launch_circuit_breaker (ctx , node_name = hook_value .value .lower () + "-" + name , hook_value = hook_value .value )
102- case HookValue .POST_NODE :
103- name = warnet_content [PLUGIN_ANNEX ][AnnexMember .NODE_NAME .value ] + "-post-pod"
104- _launch_circuit_breaker (ctx , node_name = hook_value .value .lower () + "-" + name , hook_value = hook_value .value )
92+ _launch_pod (ctx , install_name = "circuitbreaker" )
93+ case _:
94+ log .info (f"No action required for hook { hook_value } " )
10595
10696def get_data (plugin_content : dict ) -> Optional [dict ]:
10797 data = {
10898 key : plugin_content .get (key )
109- for key in (PluginContent .MAX_PENDING_HTLCS .value , PluginContent .RATE_LIMIT .value )
99+ for key in (PluginContent .POD_NAME .value , PluginContent .LND_RPC_SERVER . value , PluginContent . HTTP_LISTEN .value )
110100 if plugin_content .get (key )
111101 }
112102 return data or None
113103
104+ # def _create_secrets():
105+ # """Use local LND files for testing"""
106+ # log.info("Using local LND files for testing")
107+ # tls_cert_path = Path.home() / ".lnd" / "tls.cert"
108+ # admin_macaroon_path = Path.home() / ".lnd" / "data" / "chain" / "bitcoin" / "signet" / "admin.macaroon"
114109
115- def _launch_circuit_breaker (ctx , node_name : str , hook_value : str ):
110+ # if not tls_cert_path.exists():
111+ # raise PluginError(f"TLS certificate not found at {tls_cert_path}")
112+ # if not admin_macaroon_path.exists():
113+ # raise PluginError(f"Admin macaroon not found at {admin_macaroon_path}")
114+
115+ # log.info(f"Using TLS certificate: {tls_cert_path}")
116+ # log.info(f"Using admin macaroon: {admin_macaroon_path}")
117+
118+ # def _create_secrets():
119+ # """Create Kubernetes secrets for each LND node"""
120+ # lnd_pods = subprocess.check_output(["kubectl", "get", "pods", "-l", "mission=lightning", "-o", "name"]).decode().splitlines()
121+ # # lnd_pods = subprocess.check_output(["kubectl", "get", "pods", "-l", "app=warnet", "-l", "mission=lightning", "-o", "name"]).decode().splitlines()
122+ # for node in lnd_pods:
123+ # node_name = node.split('/')[-1]
124+ # log.info(f"Waiting for {node_name} to be ready...")
125+ # wait_for_init(node_name, namespace=get_default_namespace(), quiet=True)
126+ # log.info(f"Creating secrets for {node_name}")
127+ # subprocess.run(["kubectl", "cp", f"{node}:/root/.lnd/tls.cert", "./tls.cert"], check=True)
128+ # subprocess.run(["kubectl", "cp", f"{node}:/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon", "./admin.macaroon"], check=True)
129+ # subprocess.run(["kubectl", "create", "secret", "generic", f"lnd-tls-cert-{node_name}", "--from-file=tls.cert=./tls.cert"], check=True)
130+ # subprocess.run(["kubectl", "create", "secret", "generic", f"lnd-macaroon-{node_name}", "--from-file=admin.macaroon=./admin.macaroon"], check=True)
131+
132+ def _create_secrets ():
133+ """Create Kubernetes secrets for each LND node"""
134+ lnd_pods = subprocess .check_output (
135+ ["kubectl" , "get" , "pods" , "-l" , "mission=lightning" , "-o" , "name" ]
136+ ).decode ().splitlines ()
137+
138+ for node in lnd_pods :
139+ node_name = node .split ('/' )[- 1 ]
140+ log .info (f"Waiting for { node_name } to be ready..." )
141+
142+ # Wait for the pod to be ready
143+ max_retries = 10
144+ retry_delay = 10 # seconds
145+ for attempt in range (max_retries ):
146+ try :
147+ # Check if the pod is ready
148+ pod_status = subprocess .check_output (
149+ ["kubectl" , "get" , "pod" , node_name , "-o" , "jsonpath='{.status.phase}'" ]
150+ ).decode ().strip ("'" )
151+
152+ if pod_status == "Running" :
153+ log .info (f"{ node_name } is ready." )
154+ break
155+ else :
156+ log .info (f"{ node_name } is not ready yet (status: { pod_status } ). Retrying in { retry_delay } seconds..." )
157+ except subprocess .CalledProcessError as e :
158+ log .error (f"Failed to check pod status for { node_name } : { e } " )
159+ if attempt == max_retries - 1 :
160+ raise PluginError (f"Pod { node_name } did not become ready after { max_retries } attempts." )
161+
162+ time .sleep (retry_delay )
163+
164+ # Create secrets for the pod
165+ log .info (f"Creating secrets for { node_name } " )
166+ try :
167+ subprocess .run (
168+ ["kubectl" , "cp" , f"{ node_name } :/root/.lnd/tls.cert" , "./tls.cert" ],
169+ check = True
170+ )
171+ subprocess .run (
172+ ["kubectl" , "cp" , f"{ node_name } :/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon" , "./admin.macaroon" ],
173+ check = True
174+ )
175+ subprocess .run (
176+ ["kubectl" , "create" , "secret" , "generic" , f"lnd-tls-cert-{ node_name } " , "--from-file=tls.cert=./tls.cert" ],
177+ check = True
178+ )
179+ subprocess .run (
180+ ["kubectl" , "create" , "secret" , "generic" , f"lnd-macaroon-{ node_name } " , "--from-file=admin.macaroon=./admin.macaroon" ],
181+ check = True
182+ )
183+ except subprocess .CalledProcessError as e :
184+ log .error (f"Failed to create secrets for { node_name } : { e } " )
185+ raise PluginError (f"Failed to create secrets for { node_name } ." )
186+
187+ def _launch_pod (ctx ,
188+ install_name : str = "circuitbreaker" ,
189+ podName : str = "circuitbreaker-pod" ,
190+ rpcserver : str = "localhost:10009" ,
191+ httplisten : str = "0.0.0.0:9235" ):
116192 timestamp = int (time .time ())
117- release_name = f"cb-{ node_name } "
193+ # release_name = f"cb-{install_name }"
118194
119- # command = f"helm upgrade --install {release_name} {ctx.obj[PLUGIN_DIR_TAG]}/charts/circuitbreaker"
120195 command = (
121- f"helm upgrade --install { release_name } { ctx .obj [PLUGIN_DIR_TAG ]} /charts/circuitbreaker "
122- f"--set name= { release_name } "
196+ f"helm upgrade --install { install_name } { ctx .obj [PLUGIN_DIR_TAG ]} /charts/circuitbreaker "
197+ f"--set podName= { podName } --set rpcserver= { rpcserver } --set httplisten= { httplisten } "
123198 )
124- log .info (command )
125- run_command (command )
126199
127- if (hook_value == HookValue .POST_DEPLOY ):
128- wait_for_init (release_name , namespace = get_default_namespace (), quiet = True )
129-
200+ log .info (command )
201+ log .info (run_command (command ))
130202
131203if __name__ == "__main__" :
132204 circuitbreaker ()
0 commit comments