Anatolios Laskaris b988909a7a
chore: Allow to use different docker image (#84)
* Allow to supply different container image

* Fix container image
2022-12-26 12:18:07 +02:00

190 lines
5.4 KiB
Python

from __future__ import with_statement
from collections import namedtuple
from fabric.api import *
from fabric.contrib.files import append
from utils import *
from compose import *
from collections import namedtuple
from time import sleep
# DTOs
Node = namedtuple("Node", "peer_id tcp ws")
Service = namedtuple("Service", "port multiaddr")
FLUENCE_NODE_PORT = "7777"
FLUENCE_CLIENT_PORT = "9999"
PEER_ID_MARKER = "server peer id"
@task
@runs_once
def deploy_fluence():
# 'running', 'output'
with hide():
load_config()
target = target_environment()
env.hosts = target["bootstrap"]
puts("Fluence: deploying bootstrap")
results = execute(deploy_bootstrap)
bootstraps = fill_addresses(results.items())
bootstrap = bootstraps[0]
env.bootstraps = map(lambda b: b.tcp.multiaddr, bootstraps)
env.hosts = target["hosts"]
puts("Fluence: deploying rest of the nodes")
results = execute(deploy_nodes)
nodes = fill_addresses(results.items())
puts("Fluence: deployed.\nAddresses:\n%s" % "\n".join(
"{} {} {}".format(n.tcp.multiaddr, n.ws.multiaddr, n.peer_id) for n in nodes))
puts("Bootstrap:\n%s" % "\n".join(
"{} {} {}".format(n.tcp.multiaddr, n.ws.multiaddr, n.peer_id) for n in bootstraps))
@task
@parallel
def deploy_bootstrap():
target = target_environment()
yml = "fluence_bootstrap.yml"
keypair = get_keypairs(yml, get_host_idx(containers=1), count=1)
gen_compose_file(
out=yml,
container_image=target['container_image'],
scale=1,
is_bootstrap=True,
bootstraps=target['external_bootstraps'],
host=env.host_string,
management_key=target['management_key'],
keypairs=keypair,
ceramic_host=target['ceramic_host'],
)
return do_deploy_fluence(yml)
@task
@parallel
def deploy_nodes():
target = target_environment()
yml = "fluence.yml"
scale = target["containers_per_host"]
keypairs = get_keypairs(yml, get_host_idx(scale), count=scale)
gen_compose_file(
out=yml,
container_image=target['container_image'],
scale=scale,
is_bootstrap=False,
bootstraps=env.bootstraps + target['external_bootstraps'],
host=env.host_string,
management_key=target['management_key'],
keypairs=keypairs,
ceramic_host=target['ceramic_host'],
)
return do_deploy_fluence(yml)
@task
@parallel
# returns {ip: Node}
def do_deploy_fluence(yml="fluence.yml"):
with hide():
compose("pull -q", yml)
compose('rm -fs', yml)
compose('up --no-start', yml) # was: 'create'
copy_configs(yml)
compose("restart", yml)
sleep(5)
addrs = get_fluence_addresses(yml)
return addrs
def get_host_idx(containers):
return env.hosts.index(env.host_string) * containers
def copy_configs(yml):
# there's no `cp` in `docker-compose`: https://github.com/docker/compose/issues/5523
put("Config.toml", "./")
containers = compose('ps -q', yml).splitlines()
for id in containers:
run('docker cp ./Config.toml %s:/Config.toml' % id)
# returns [Node]
def get_fluence_addresses(yml="fluence.yml"):
containers = compose('ps -q', yml).splitlines()
nodes = []
for id in containers:
(tcp_port, ws_port) = get_ports(id)
peer_id = get_fluence_peer_ids(id)
node = Node(peer_id=peer_id, tcp=Service(tcp_port, None), ws=Service(ws_port, None))
nodes.append(node)
return nodes
# Assuming Fluence's tcp port starts with 7
# and websocket port starts with 9
def is_fluence_port(host_port):
is_tcp = '0.0.0.0:7' in host_port
is_ws = '0.0.0.0:9' in host_port
return is_tcp or is_ws
# returns (tcp port, ws port)
def get_ports(container):
from itertools import chain
lines = run('docker port %s' % container).splitlines()
ports = chain.from_iterable(l.split('/tcp -> ') for l in lines)
# filter by host port and remove 0.0.0.0 part
ports = list(port.replace('0.0.0.0:', '') for port in ports if is_fluence_port(port))
(a, b) = ports
# tcp port starts with 7
if a.startswith('7'):
return (a, b)
else:
return (b, a)
def get_fluence_peer_ids(container, yml="fluence.yml"):
logs = run('docker logs --tail 10000 %s' % container).splitlines()
return parse_peer_ids(logs)
# returns (node_peer_id, peer_peer_id)
def parse_peer_ids(logs):
def after_eq(line):
return line.split("=")[-1].strip()
peer_id = None
for line in logs:
if PEER_ID_MARKER in line:
peer_id = after_eq(line)
return peer_id
def compose(cmd, yml="fluence.yml"):
return run('docker-compose -f %s %s' % (yml, cmd))
def service(yml):
return yml.replace(".yml", "")
# takes: dict {ip: Node}
# returns: [Node]
def fill_addresses(nodes_dict):
result = []
for ip, nodes in nodes_dict:
for node in nodes:
# node service multiaddr
node = node._replace(tcp=fill_multiaddr(ip, node.tcp))
# peer service multiaddr
node = node._replace(ws=fill_multiaddr(ip, node.ws, suffix="/ws"))
result.append(node)
return result
def fill_multiaddr(ip, service, suffix=""):
return service._replace(multiaddr="/ip4/{}/tcp/{}{}".format(ip, service.port, suffix))