Skip to content

Commit bbd81bc

Browse files
committed
simln: export data inside kubernetes network
1 parent 5c32b19 commit bbd81bc

File tree

5 files changed

+85
-17
lines changed

5 files changed

+85
-17
lines changed

src/backends/backend_interface.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,20 @@ def get_tank_ipv4(self, index: int) -> str:
109109
"""
110110
raise NotImplementedError("This method should be overridden by child class")
111111

112+
@abstractmethod
113+
def get_lnnode_hostname(self, index: int) -> str:
114+
"""
115+
Get the hostname assigned to a lnnode attached to a tank from the backend
116+
"""
117+
raise NotImplementedError("This method should be overridden by child class")
118+
112119
@abstractmethod
113120
def wait_for_healthy_tanks(self, warnet, timeout=60) -> bool:
114121
"""
115122
Wait for healthy status on all bitcoind nodes
116123
"""
117124
raise NotImplementedError("This method should be overridden by child class")
118125

119-
120126
@abstractmethod
121127
def service_from_json(self, json: object):
122128
"""

src/backends/compose/compose_backend.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,12 @@ def add_services(self, tank: Tank, compose):
356356
"networks": [tank.network_name],
357357
}
358358

359+
def get_lnnode_hostname(self, index: int) -> str:
360+
return self.get_container_name(index, ServiceType.LIGHTNING)
361+
359362
def add_lnd_service(self, tank, compose):
360363
services = compose["services"]
361-
ln_container_name = self.get_container_name(tank.index, ServiceType.LIGHTNING)
364+
ln_container_name = self.get_lnnode_hostname(tank.index)
362365
ln_cb_container_name = self.get_container_name(tank.index, ServiceType.CIRCUITBREAKER)
363366
bitcoin_container_name = self.get_container_name(tank.index, ServiceType.BITCOIN)
364367
# These args are appended to the Dockerfile `ENTRYPOINT ["lnd"]`

src/backends/kubernetes/kubernetes_backend.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import re
44
import time
55
from pathlib import Path
6-
from typing import cast
6+
from typing import IO, cast
77

88
import yaml
99
from backends import BackendInterface, ServiceType
@@ -441,12 +441,15 @@ def remove_prometheus_service_monitors(self, tanks):
441441
except ResourceNotFoundError:
442442
continue
443443

444+
def get_lnnode_hostname(self, index: int) -> str:
445+
return f"lightning-{index}.{self.namespace}"
446+
444447
def create_lnd_container(
445448
self, tank, bitcoind_service_name, volume_mounts
446449
) -> client.V1Container:
447450
# These args are appended to the Dockerfile `ENTRYPOINT ["lnd"]`
448451
bitcoind_rpc_host = f"{bitcoind_service_name}.{self.namespace}"
449-
lightning_dns = f"lightning-{tank.index}.{self.namespace}"
452+
lightning_dns = self.get_lnnode_hostname(tank.index)
450453
args = tank.lnnode.get_conf(lightning_dns, bitcoind_rpc_host)
451454
self.log.debug(f"Creating lightning container for tank {tank.index} using {args=:}")
452455
lightning_container = client.V1Container(
@@ -714,6 +717,14 @@ def service_from_json(self, obj):
714717
for pair in obj.get("environment", []):
715718
name, value = pair.split("=")
716719
env.append(client.V1EnvVar(name=name, value=value))
720+
volume_mounts = []
721+
volumes = []
722+
for vol in obj.get("config_files", []):
723+
volume_name, mount_path = vol.split(":")
724+
volume_name = volume_name.replace("/", "")
725+
volume_mounts.append(client.V1VolumeMount(name=volume_name, mount_path=mount_path))
726+
volumes.append(client.V1Volume(name=volume_name, empty_dir=client.V1EmptyDirVolumeSource()))
727+
717728
service_container = client.V1Container(
718729
name=obj["container_name_suffix"],
719730
image=obj["image"],
@@ -722,6 +733,13 @@ def service_from_json(self, obj):
722733
privileged=True,
723734
capabilities=client.V1Capabilities(add=["NET_ADMIN", "NET_RAW"]),
724735
),
736+
volume_mounts=volume_mounts
737+
)
738+
sidecar_container = client.V1Container(
739+
name="sidecar",
740+
image="alpine:latest",
741+
command=["/bin/sh", "-c", "sleep infinity"],
742+
volume_mounts=volume_mounts
725743
)
726744
service_pod = client.V1Pod(
727745
api_version="v1",
@@ -736,8 +754,41 @@ def service_from_json(self, obj):
736754
),
737755
spec=client.V1PodSpec(
738756
restart_policy="OnFailure",
739-
containers=[service_container],
740-
volumes=[],
757+
containers=[service_container, sidecar_container],
758+
volumes=volumes,
741759
),
742760
)
743761
self.client.create_namespaced_pod(namespace=self.namespace, body=service_pod)
762+
763+
def write_service_config(self, service_name: str, tar_buffer: IO[bytes], destination_path: str):
764+
obj = services[service_name]
765+
pod_name = obj["container_name_suffix"]
766+
container_name = "sidecar"
767+
# Write the archive
768+
resp = stream(
769+
self.client.connect_get_namespaced_pod_exec,
770+
pod_name,
771+
self.namespace,
772+
container=container_name,
773+
command=["/bin/sh", "-c", "cat > /arbitrary_filename.tar"],
774+
stderr=True,
775+
stdin=True,
776+
stdout=True,
777+
tty=False,
778+
_preload_content=False
779+
)
780+
resp.write_stdin(tar_buffer.getvalue())
781+
resp.close()
782+
# Unpack the archive
783+
stream(
784+
self.client.connect_get_namespaced_pod_exec,
785+
pod_name,
786+
self.namespace,
787+
container=container_name,
788+
command=["/bin/sh", "-c", f"tar -xf /arbitrary_filename.tar -C {destination_path}"],
789+
stderr=True,
790+
stdin=False,
791+
stdout=True,
792+
tty=False,
793+
_preload_content=False
794+
)

src/warnet/lnnode.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,12 @@ def export(self, config: object, tar_file):
128128
ServiceType.LIGHTNING,
129129
"/root/.lnd/tls.cert"
130130
)
131+
name = f"ln-{self.tank.index}"
132+
macaroon_filename = f"{name}_admin.macaroon"
133+
cert_filename = f"{name}_tls.cert"
134+
host = self.backend.get_lnnode_hostname(self.tank.index)
131135

132136
# Add the files to the in-memory tar archive
133-
container_name = self.backend.get_container_name(self.tank.index, ServiceType.LIGHTNING)
134-
macaroon_filename = f"{container_name}_admin.macaroon"
135-
cert_filename = f"{container_name}_tls.cert"
136-
137137
tarinfo1 = tarfile.TarInfo(name=macaroon_filename)
138138
tarinfo1.size = len(macaroon)
139139
fileobj1 = io.BytesIO(macaroon)
@@ -145,8 +145,8 @@ def export(self, config: object, tar_file):
145145

146146
config["nodes"].append(
147147
{
148-
"id": container_name,
149-
"address": f"https://{container_name}:{self.rpc_port}",
148+
"id": name,
149+
"address": f"https://{host}:{self.rpc_port}",
150150
"macaroon": f"/simln/{macaroon_filename}",
151151
"cert": f"/simln/{cert_filename}",
152152
}

src/warnet/server.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ def network_export(self, network: str) -> bool:
275275
Export all data for a simln container running on the network
276276
"""
277277
wn = self.get_warnet(network)
278+
if "simln" not in wn.services:
279+
raise Exception("No simln service in network")
278280

279281
# JSON object that will eventually be written to simln config file
280282
config = {"nodes": []}
@@ -290,11 +292,17 @@ def network_export(self, network: str) -> bool:
290292
tarinfo.size = len(config_bytes)
291293
tar_file.addfile(tarinfo=tarinfo, fileobj=config_stream)
292294

293-
with open(wn.config_dir / "simln.tar", "wb") as output:
294-
tar_buffer.seek(0)
295-
output.write(tar_buffer.read())
296-
297-
subprocess.run(["tar", "-xf", wn.config_dir / 'simln.tar', "-C", wn.config_dir / 'simln'])
295+
if self.backend == "compose":
296+
# Write the archive to the RPC server's config directory
297+
with open(wn.config_dir / "simln.tar", "wb") as output:
298+
tar_buffer.seek(0)
299+
output.write(tar_buffer.read())
300+
# Extract the archive into a subdirectory that is already
301+
# shared with the simln container as a volume
302+
subprocess.run(["tar", "-xf", wn.config_dir / 'simln.tar', "-C", wn.config_dir / 'simln'])
303+
if self.backend == "k8s":
304+
# Write the archive to the "emptydir" volume in the simln pod
305+
wn.container_interface.write_service_config("simln", tar_buffer, "/simln/")
298306
return True
299307

300308
def scenarios_available(self) -> list[tuple]:

0 commit comments

Comments
 (0)