Skip to content

Commit

Permalink
Merge pull request bitcoin-dev-project#281 from willcl-ark/connect-wi…
Browse files Browse the repository at this point in the history
…th-addnode

server: make all connections with addnode
  • Loading branch information
m3dwards authored Feb 22, 2024
2 parents b58c8e9 + 98f81e3 commit 67960e5
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 39 deletions.
7 changes: 7 additions & 0 deletions src/backends/backend_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,10 @@ def warnet_from_deployment(self, warnet):
Rebuild a warnet object from an active deployment
"""
raise NotImplementedError("This method should be overridden by child class")

@abstractmethod
def wait_for_healthy_tanks(self, warnet, timeout=60) -> bool:
"""
Wait for healthy status on all bitcoind nodes
"""
raise NotImplementedError("This method should be overridden by child class")
38 changes: 36 additions & 2 deletions src/backends/compose/compose_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import shutil
import subprocess
import time
from datetime import datetime
from pathlib import Path
from typing import cast
Expand Down Expand Up @@ -49,7 +50,8 @@ class ComposeBackend(BackendInterface):
def __init__(self, config_dir: Path, network_name: str) -> None:
super().__init__(config_dir)
self.network_name = network_name
self.client = docker.DockerClient = docker.from_env()
self.client: docker.DockerClient = docker.from_env()
self._apiclient: docker.APIClient = docker.APIClient(base_url='unix://var/run/docker.sock')

def build(self) -> bool:
command = ["docker", "compose", "build"]
Expand Down Expand Up @@ -390,7 +392,7 @@ def add_services(self, tank: Tank, services):
"privileged": True,
"cap_add": ["NET_ADMIN", "NET_RAW"],
"healthcheck": {
"test": ["CMD", "pidof", "bitcoind"],
"test": ["CMD-SHELL", f"nc -z localhost {tank.rpc_port} || exit 1"],
"interval": "10s", # Check every 10 seconds
"timeout": "1s", # Give the check 1 second to complete
"start_period": "5s", # Start checking after 5 seconds
Expand Down Expand Up @@ -511,3 +513,35 @@ def get_ipv4_address(self, container: Container) -> str:
"""
container_inspect = self.client.containers.get(container.id).attrs
return container_inspect['NetworkSettings']['Networks'][self.network_name]['IPAddress']

def get_container_health(self, container: Container):
c_inspect = self._apiclient.inspect_container(container.name)
return c_inspect["State"]["Health"]["Status"]

def check_health_all_bitcoind(self, warnet) -> bool:
"""
Checks the health of all bitcoind containers
"""
status = ["unhealthy"] * len(warnet.tanks)

for tank in warnet.tanks:
status[tank.index] = self.get_container_health(
self.get_container(tank.index, ServiceType.BITCOIN)
)
logger.debug(f"Tank healthcheck: {status}")

return status[0] == "healthy" and all(i == status[0] for i in status)

def wait_for_healthy_tanks(self, warnet, timeout=60) -> bool:
start = time.time()
healthy = False
logger.debug("Waiting for all tanks to reach healthy")

while not healthy and (time.time() < start + timeout):
healthy = self.check_health_all_bitcoind(warnet)
time.sleep(2)

if not healthy:
raise Exception(f"Tanks did not reach healthy status in {timeout} seconds")

return healthy
6 changes: 6 additions & 0 deletions src/backends/kubernetes/kubernetes_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,3 +703,9 @@ def deploy_pods(self, warnet):
yaml.dump(pod.to_dict(), f)
f.write("---\n") # separator for multiple resources
self.log.info("Pod definitions saved to warnet-tanks.yaml")

def wait_for_healthy_tanks(self, warnet, timeout=30):
"""
Wait for healthy status on all bitcoind nodes
"""
pass
2 changes: 2 additions & 0 deletions src/warnet/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ def thread_start(wn):
# Update warnet from docker here to get ip addresses
wn = Warnet.from_network(network, self.backend)
wn.apply_network_conditions()
wn.wait_for_health()
wn.connect_edges()
self.logger.info(
f"Resumed warnet named '{network}' from config dir {wn.config_dir}"
Expand Down Expand Up @@ -384,6 +385,7 @@ def thread_start(wn, lock: threading.Lock):
# wn.write_fork_observer_config()
wn.warnet_build()
wn.warnet_up()
wn.wait_for_health()
wn.apply_network_conditions()
wn.connect_edges()
except Exception as e:
Expand Down
28 changes: 0 additions & 28 deletions src/warnet/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,34 +372,6 @@ def remove_version_prefix(version_str):
return version_str


def version_cmp_ge(version_str, target_str):
parsed_version_str = remove_version_prefix(version_str)
parsed_target_str = remove_version_prefix(target_str)

try:
version_parts = list(map(int, parsed_version_str.split(".")))
target_parts = list(map(int, parsed_target_str.split(".")))

# Pad the shorter version with zeros
while len(version_parts) < len(target_parts):
version_parts.append(0)
while len(target_parts) < len(version_parts):
target_parts.append(0)

# handle custom versions
except ValueError:
logger.debug(
ValueError(
f"Unknown version string: {version_str} or {target_str} could not be compared"
)
)
logger.debug("Assuming custom version can use `addpeeraddress`")
# assume that custom versions are recent
return True

return version_parts >= target_parts


def set_execute_permission(file_path):
current_permissions = os.stat(file_path).st_mode
os.chmod(file_path, current_permissions | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
Expand Down
15 changes: 6 additions & 9 deletions src/warnet/warnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from backends import ComposeBackend, KubernetesBackend
from templates import TEMPLATES
from warnet.tank import Tank
from warnet.utils import gen_config_dir, version_cmp_ge
from warnet.utils import gen_config_dir

logger = logging.getLogger("warnet")
FO_CONF_NAME = "fork_observer_config.toml"
Expand Down Expand Up @@ -172,14 +172,8 @@ def connect_edges(self):
continue
src_tank = self.tanks[src]
dst_ip = self.tanks[dst].ipv4
# <= 20.2 doesn't have addpeeraddress
res = version_cmp_ge(src_tank.version, "0.21.0")
if res:
cmd = f"bitcoin-cli -regtest -rpcuser={src_tank.rpc_user} -rpcpassword={src_tank.rpc_password} addpeeraddress {dst_ip} 18444"
logger.info(f"Using `{cmd}` to connect tanks {src} to {dst}")
else:
cmd = f'bitcoin-cli -regtest -rpcuser={src_tank.rpc_user} -rpcpassword={src_tank.rpc_password} addnode "{dst_ip}:18444" onetry'
logger.info(f"Using `{cmd}` to connect tanks {src} to {dst}")
cmd = f"bitcoin-cli -regtest -rpcuser={src_tank.rpc_user} -rpcpassword={src_tank.rpc_password} addnode {dst_ip}:18444 onetry"
logger.info(f"Using `{cmd}` to connect tanks {src} to {dst}")
src_tank.exec(cmd=cmd)

def warnet_build(self):
Expand Down Expand Up @@ -224,3 +218,6 @@ def export(self, subdir):
config_path = os.path.join(subdir, "sim.json")
with open(config_path, "a") as f:
json.dump(config, f)

def wait_for_health(self):
self.container_interface.wait_for_healthy_tanks(self)

0 comments on commit 67960e5

Please sign in to comment.