Skip to content

Commit 070f3b6

Browse files
authored
Merge pull request #2 from Camillarhi/circuitbreaker4
Circuitbreaker4
2 parents f7d4bd9 + cb50c54 commit 070f3b6

File tree

8 files changed

+198
-80
lines changed

8 files changed

+198
-80
lines changed

resources/networks/hello/network.yaml

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,6 @@ plugins: # Each plugin section has a number of hooks available (preDeploy, post
5959
entrypoint: "../../plugins/hello" # This entrypoint path is relative to the network.yaml file
6060
podName: "hello-pre-deploy"
6161
helloTo: "preDeploy!"
62-
circuitbreaker:
63-
entrypoint: "../../plugins/circuitbreaker"
64-
url: "http://127.0.0.1:9235"
65-
apiUrl: "/api"
66-
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
67-
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
68-
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
6962
postDeploy:
7063
hello:
7164
entrypoint: "../../plugins/hello"
@@ -76,54 +69,24 @@ plugins: # Each plugin section has a number of hooks available (preDeploy, post
7669
activity: '[{"source": "tank-0003-ln", "destination": "tank-0005-ln", "interval_secs": 1, "amount_msat": 2000}]'
7770
circuitbreaker:
7871
entrypoint: "../../plugins/circuitbreaker"
79-
url: "http://127.0.0.1:9235"
80-
apiUrl: "/api"
81-
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
82-
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
83-
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
72+
podName: "circuitbreaker-pod"
73+
rpcserver: "172.29.34.166:10009"
74+
httplisten: "0.0.0.0:9235"
8475
preNode: # preNode plugins run before each node is deployed
8576
hello:
8677
entrypoint: "../../plugins/hello"
8778
helloTo: "preNode!"
88-
circuitbreaker:
89-
entrypoint: "../../plugins/circuitbreaker"
90-
url: "http://127.0.0.1:9235"
91-
apiUrl: "/api"
92-
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
93-
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
94-
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
9579
postNode:
9680
hello:
9781
entrypoint: "../../plugins/hello"
9882
helloTo: "postNode!"
99-
circuitbreaker:
100-
entrypoint: "../../plugins/circuitbreaker"
101-
url: "http://127.0.0.1:9235"
102-
apiUrl: "/api"
103-
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
104-
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
105-
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
10683
preNetwork:
10784
hello:
10885
entrypoint: "../../plugins/hello"
10986
helloTo: "preNetwork!"
11087
podName: "hello-pre-network"
111-
circuitbreaker:
112-
entrypoint: "../../plugins/circuitbreaker"
113-
url: "http://127.0.0.1:9235"
114-
apiUrl: "/api"
115-
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
116-
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
117-
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
11888
postNetwork:
11989
hello:
12090
entrypoint: "../../plugins/hello"
12191
helloTo: "postNetwork!"
122-
podName: "hello-post-network"
123-
circuitbreaker:
124-
entrypoint: "../../plugins/circuitbreaker"
125-
url: "http://127.0.0.1:9235"
126-
apiUrl: "/api"
127-
mode: "fail" # Operating mode: fail, queue, or queue_peer_initiated
128-
maxPendingHtlcs: 10 # Default maximum pending HTLCs per peer
129-
rateLimit: 1 # Minimum seconds between HTLCs (token bucket rate limit)
92+
podName: "hello-post-network"

resources/plugins/circuitbreaker/charts/circuitbreaker/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ apiVersion: v2
22
name: circuitbreaker
33
description: A Helm chart to deploy Circuit Breaker
44
version: 0.1.0
5-
appVersion: "0.1.0"
5+
appVersion: "0.1.0"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{- define "mychart.name" -}}
2+
{{- .Chart.Name | trunc 63 | trimSuffix "-" -}}
3+
{{- end -}}
4+
5+
{{- define "mychart.fullname" -}}
6+
{{- printf "%s-%s" (include "mychart.name" .) .Release.Name | trunc 63 | trimSuffix "-" -}}
7+
{{- end -}}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ include "mychart.fullname" . }}-data
5+
data:
6+
tls.cert: |
7+
-----BEGIN CERTIFICATE-----
8+
MIICRTCCAeygAwIBAgIRAMe5IfFsBM9nqG1hwA1tswAwCgYIKoZIzj0EAwIwOzEf
9+
MB0GA1UEChMWbG5kIGF1dG9nZW5lcmF0ZWQgY2VydDEYMBYGA1UEAxMPREVTS1RP
10+
UC00OEJVR0xTMB4XDTI1MDIwMTE2NDAyNFoXDTI2MDMyOTE2NDAyNFowOzEfMB0G
11+
A1UEChMWbG5kIGF1dG9nZW5lcmF0ZWQgY2VydDEYMBYGA1UEAxMPREVTS1RPUC00
12+
OEJVR0xTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIl4bWvtGVb1T4iUyjLfj
13+
U2IVnF1yJBwbTa2diRJh+a0UbwjUSdn/hIVkNALr9f3NKYWmotyq8IGOmjwhAFis
14+
HKOB0DCBzTAOBgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYD
15+
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU38wWLmz1lsVv7vtZZamSgkcoQUcwdgYD
16+
VR0RBG8wbYIPREVTS1RPUC00OEJVR0xTgglsb2NhbGhvc3SCBHVuaXiCCnVuaXhw
17+
YWNrZXSCB2J1ZmNvbm6HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAGHBAr///6HBKwd
18+
IqaHEP6AAAAAAAAAAhVd//6GM+MwCgYIKoZIzj0EAwIDRwAwRAIgNe9zoH9iz7Tw
19+
1j8+Jk05DU6nJ48a5mbP0viZ50UGu7sCIEK0AoPBrqxnicdhEEInONWyIm5VUR/l
20+
YURZZyNuJ8lJ
21+
-----END CERTIFICATE-----
22+
admin.macaroon.hex: |
23+
0201036C6E6402F801030A107EC4D3E96DE93FA58F70968A1729AE6C1201301A160A0761646472657373120472656164120577726974651A130A04696E666F120472656164120577726974651A170A08696E766F69636573120472656164120577726974651A210A086D616361726F6F6E120867656E6572617465120472656164120577726974651A160A076D657373616765120472656164120577726974651A170A086F6666636861696E120472656164120577726974651A160A076F6E636861696E120472656164120577726974651A140A057065657273120472656164120577726974651A180A067369676E6572120867656E65726174651204726561640000062023AFC3BF7DB1D186342905D79461793FFCB59F583858F495C253F0A1EB4D33C2
Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,46 @@
11
apiVersion: v1
22
kind: Pod
33
metadata:
4-
name: {{ .Values.name }}
4+
name: {{ include "mychart.fullname" . }}
55
labels:
6-
app: {{ .Chart.Name }}
6+
app: {{ include "mychart.name" . }}
7+
mission: {{ .Values.name }}
78
spec:
9+
initContainers:
10+
- name: "init"
11+
image: "busybox"
12+
command:
13+
- "sh"
14+
- "-c"
15+
args:
16+
- >
17+
mkdir -p /shared/.lnd/data/chain/bitcoin/mainnet &&
18+
cp /configmap/tls.cert /shared/.lnd/tls.cert &&
19+
cat /configmap/admin.macaroon.hex | xxd -r -p > /shared/.lnd/data/chain/bitcoin/mainnet/admin.macaroon
20+
volumeMounts:
21+
- name: shared-volume
22+
mountPath: /shared
23+
- name: configmap-volume
24+
mountPath: /configmap
825
containers:
9-
- name: {{ .Values.name }}-container
26+
- name: {{ .Values.name }}
1027
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
1128
imagePullPolicy: {{ .Values.image.pullPolicy }}
12-
command: ["sh", "-c"]
29+
command:
30+
- "sh"
31+
- "-c"
1332
args:
14-
- echo "Hello {{ .Values.name }}";
15-
resources: {}
16-
33+
- >
34+
mkdir -p /root/.lnd/data/chain/bitcoin/mainnet &&
35+
ln -s /shared/.lnd/tls.cert /root/.lnd/tls.cert &&
36+
ln -s /shared/.lnd/data/chain/bitcoin/mainnet/admin.macaroon /root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon &&
37+
circuitbreaker --rpcserver={{ .Values.lnd.rpcserver }} --httplisten={{ .Values.lnd.httplisten }}
38+
volumeMounts:
39+
- name: shared-volume
40+
mountPath: /shared
41+
volumes:
42+
- name: configmap-volume
43+
configMap:
44+
name: {{ include "mychart.fullname" . }}-data
45+
- name: shared-volume
46+
emptyDir: {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: {{ include "mychart.fullname" . }}-service
5+
labels:
6+
app: {{ include "mychart.name" . }}
7+
spec:
8+
type: NodePort
9+
ports:
10+
- port: 9235
11+
targetPort: 9235
12+
nodePort: 30000 # Choose a port between 30000-32767
13+
selector:
14+
app: {{ include "mychart.name" . }}

resources/plugins/circuitbreaker/charts/circuitbreaker/values.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@ name: "circuitbreaker"
22
image:
33
repository: "camillarhi/circuitbreaker"
44
tag: "latest"
5-
pullPolicy: IfNotPresent
5+
pullPolicy: IfNotPresent
6+
workingVolume:
7+
name: working-volume
8+
mountPath: /working
9+
configmapVolume:
10+
name: configmap-volume
11+
mountPath: /configmap
12+
lnd:
13+
rpcserver: "172.29.34.166:10009" # Default LND RPC server address
14+
httplisten: "0.0.0.0:9235" # Default HTTP listen address

resources/plugins/circuitbreaker/plugin.py

Lines changed: 102 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
from enum import Enum
55
from pathlib import Path
6+
import subprocess
67
import time
78
from typing import Optional
89

@@ -25,11 +26,9 @@
2526

2627
PLUGIN_DIR_TAG = "plugin_dir"
2728

28-
2929
class PluginError(Exception):
3030
pass
3131

32-
3332
log = logging.getLogger(MISSION)
3433
if not log.hasHandlers():
3534
console_handler = logging.StreamHandler()
@@ -41,9 +40,9 @@ class PluginError(Exception):
4140
log.propagate = True
4241

4342
class 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

10696
def 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

131203
if __name__ == "__main__":
132204
circuitbreaker()

0 commit comments

Comments
 (0)