|
1 | 1 | import base64 |
2 | 2 | import logging |
3 | 3 | import re |
| 4 | +import subprocess |
4 | 5 | import time |
5 | 6 | from pathlib import Path |
6 | 7 | from typing import cast |
|
15 | 16 | from kubernetes.dynamic import DynamicClient |
16 | 17 | from kubernetes.dynamic.exceptions import ResourceNotFoundError |
17 | 18 | from kubernetes.stream import stream |
| 19 | +from warnet.services import services |
18 | 20 | from warnet.status import RunningStatus |
19 | 21 | from warnet.tank import Tank |
20 | 22 | from warnet.utils import parse_raw_messages |
@@ -73,6 +75,10 @@ def down(self, warnet) -> bool: |
73 | 75 |
|
74 | 76 | self.remove_prometheus_service_monitors(warnet.tanks) |
75 | 77 |
|
| 78 | + for service_name in warnet.services: |
| 79 | + if "k8s" in services[service_name]["backends"]: |
| 80 | + self.client.delete_namespaced_pod(f'{services[service_name]["container_name_suffix"]}-service', self.namespace) |
| 81 | + |
76 | 82 | return True |
77 | 83 |
|
78 | 84 | def get_file(self, tank_index: int, service: ServiceType, file_path: str): |
@@ -436,12 +442,15 @@ def remove_prometheus_service_monitors(self, tanks): |
436 | 442 | except ResourceNotFoundError: |
437 | 443 | continue |
438 | 444 |
|
| 445 | + def get_lnnode_hostname(self, index: int) -> str: |
| 446 | + return f"lightning-{index}.{self.namespace}" |
| 447 | + |
439 | 448 | def create_lnd_container( |
440 | 449 | self, tank, bitcoind_service_name, volume_mounts |
441 | 450 | ) -> client.V1Container: |
442 | 451 | # These args are appended to the Dockerfile `ENTRYPOINT ["lnd"]` |
443 | 452 | bitcoind_rpc_host = f"{bitcoind_service_name}.{self.namespace}" |
444 | | - lightning_dns = f"lightning-{tank.index}.{self.namespace}" |
| 453 | + lightning_dns = self.get_lnnode_hostname(tank.index) |
445 | 454 | args = tank.lnnode.get_conf(lightning_dns, bitcoind_rpc_host) |
446 | 455 | self.log.debug(f"Creating lightning container for tank {tank.index} using {args=:}") |
447 | 456 | lightning_container = client.V1Container( |
@@ -668,6 +677,10 @@ def deploy_pods(self, warnet): |
668 | 677 | if self.check_logging_crds_installed(): |
669 | 678 | self.apply_prometheus_service_monitors(warnet.tanks) |
670 | 679 |
|
| 680 | + for service_name in warnet.services: |
| 681 | + if "k8s" in services[service_name]["backends"]: |
| 682 | + self.service_from_json(services[service_name]) |
| 683 | + |
671 | 684 | self.log.debug("Containers and services created. Configuring IP addresses") |
672 | 685 | # now that the pods have had a second to create, |
673 | 686 | # get the ips and set them on the tanks |
@@ -700,5 +713,100 @@ def wait_for_healthy_tanks(self, warnet, timeout=30): |
700 | 713 | """ |
701 | 714 | pass |
702 | 715 |
|
703 | | - def service_from_json(self, obj: dict) -> dict: |
704 | | - pass |
| 716 | + def service_from_json(self, obj): |
| 717 | + env = [] |
| 718 | + for pair in obj.get("environment", []): |
| 719 | + name, value = pair.split("=") |
| 720 | + env.append(client.V1EnvVar(name=name, value=value)) |
| 721 | + volume_mounts = [] |
| 722 | + volumes = [] |
| 723 | + for vol in obj.get("config_files", []): |
| 724 | + volume_name, mount_path = vol.split(":") |
| 725 | + volume_name = volume_name.replace("/", "") |
| 726 | + volume_mounts.append(client.V1VolumeMount(name=volume_name, mount_path=mount_path)) |
| 727 | + volumes.append(client.V1Volume(name=volume_name, empty_dir=client.V1EmptyDirVolumeSource())) |
| 728 | + |
| 729 | + service_container = client.V1Container( |
| 730 | + name=obj["container_name_suffix"], |
| 731 | + image=obj["image"], |
| 732 | + env=env, |
| 733 | + security_context=client.V1SecurityContext( |
| 734 | + privileged=True, |
| 735 | + capabilities=client.V1Capabilities(add=["NET_ADMIN", "NET_RAW"]), |
| 736 | + ), |
| 737 | + volume_mounts=volume_mounts |
| 738 | + ) |
| 739 | + sidecar_container = client.V1Container( |
| 740 | + name="sidecar", |
| 741 | + image="pinheadmz/sidecar:latest", |
| 742 | + volume_mounts=volume_mounts, |
| 743 | + ports=[client.V1ContainerPort(container_port=22)], |
| 744 | + ) |
| 745 | + service_pod = client.V1Pod( |
| 746 | + api_version="v1", |
| 747 | + kind="Pod", |
| 748 | + metadata=client.V1ObjectMeta( |
| 749 | + name=obj["container_name_suffix"], |
| 750 | + namespace=self.namespace, |
| 751 | + labels={ |
| 752 | + "app": obj["container_name_suffix"], |
| 753 | + "network": self.network_name, |
| 754 | + }, |
| 755 | + ), |
| 756 | + spec=client.V1PodSpec( |
| 757 | + restart_policy="OnFailure", |
| 758 | + containers=[service_container, sidecar_container], |
| 759 | + volumes=volumes, |
| 760 | + ), |
| 761 | + ) |
| 762 | + |
| 763 | + # Do not ever change this variable name. xoxo, --Zip |
| 764 | + service_service = client.V1Service( |
| 765 | + api_version="v1", |
| 766 | + kind="Service", |
| 767 | + metadata=client.V1ObjectMeta( |
| 768 | + name=f'{obj["container_name_suffix"]}-service', |
| 769 | + labels={ |
| 770 | + "app": obj["container_name_suffix"], |
| 771 | + "network": self.network_name, |
| 772 | + }, |
| 773 | + ), |
| 774 | + spec=client.V1ServiceSpec( |
| 775 | + selector={"app": obj["container_name_suffix"]}, |
| 776 | + publish_not_ready_addresses=True, |
| 777 | + ports=[ |
| 778 | + client.V1ServicePort(name="ssh", port=22, target_port=22), |
| 779 | + ] |
| 780 | + ) |
| 781 | + ) |
| 782 | + |
| 783 | + self.client.create_namespaced_pod(namespace=self.namespace, body=service_pod) |
| 784 | + self.client.create_namespaced_service(namespace=self.namespace, body=service_service) |
| 785 | + |
| 786 | + def write_service_config(self, source_path: str, service_name: str, destination_path: str): |
| 787 | + obj = services[service_name] |
| 788 | + name = obj["container_name_suffix"] |
| 789 | + container_name = "sidecar" |
| 790 | + # Copy the archive from our local drive (Warnet RPC container/pod) |
| 791 | + # to the destination service's sidecar container via ssh |
| 792 | + self.log.info(f"Copying local {source_path} to remote {destination_path} for {service_name}") |
| 793 | + subprocess.run([ |
| 794 | + "scp", |
| 795 | + "-o", "StrictHostKeyChecking=accept-new", |
| 796 | + source_path, |
| 797 | + f"root@{name}-service.{self.namespace}:/arbitrary_filename.tar"]) |
| 798 | + self.log.info(f"Finished copying tarball for {service_name}, unpacking...") |
| 799 | + # Unpack the archive |
| 800 | + stream( |
| 801 | + self.client.connect_get_namespaced_pod_exec, |
| 802 | + name, |
| 803 | + self.namespace, |
| 804 | + container=container_name, |
| 805 | + command=["/bin/sh", "-c", f"tar -xf /arbitrary_filename.tar -C {destination_path}"], |
| 806 | + stderr=True, |
| 807 | + stdin=False, |
| 808 | + stdout=True, |
| 809 | + tty=False, |
| 810 | + _preload_content=False |
| 811 | + ) |
| 812 | + self.log.info(f"Finished unpacking config data for {service_name} to {destination_path}") |
0 commit comments