Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions src/clients/launcher.star
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Module for launching the PQ devnet.
"""

ream_launcher = import_module("./ream/ream_launcher.star")
zeam_launcher = import_module("./zeam/zeam_launcher.star")

def prelaunch(plan, participants, keys_artifacts):
"""
Expand All @@ -24,13 +25,18 @@ def prelaunch(plan, participants, keys_artifacts):
client_type = participant.get("type")
client_image = participant.get("image", "")
client_count = participant.get("count", 1)

# TODO: Support other client types
if client_type != "ream":

# TODO: Add Qlean
if client_type == "ream":
launcher = ream_launcher
elif client_type == "zeam":
launcher = zeam_launcher
else:
plan.print("Unsupported client type: {}".format(client_type))
continue

for _ in range(client_count):
service = ream_launcher.initialize(
service = launcher.initialize(
plan,
client_image,
node_index,
Expand Down Expand Up @@ -77,12 +83,29 @@ def launch(plan, services, genesis_artifacts):
},
description = "Reading config.yaml from genesis artifacts",
)
validator_config_yaml_result = plan.run_sh(
run = "cat /genesis/validator-config.yaml",
files = {
"/genesis": genesis_artifacts.validator_config,
},
description = "Reading validator-config.yaml from genesis artifacts",
)
artifacts_content = struct(
nodes_yaml = nodes_yaml_result.output,
validators_yaml = validators_yaml_result.output,
config_yaml = config_yaml_result.output,
validator_config_yaml = validator_config_yaml_result.output,
)

for i, service in enumerate(services):
# TODO: Support other client types
ream_launcher.start(plan, service, i, artifacts_content)
client_type = service.name.split("-")[0]

# TODO: Add Qlean
if client_type == "ream":
launcher = ream_launcher
elif client_type == "zeam":
launcher = zeam_launcher
else:
plan.print("Unsupported client type during launch: {}".format(client_type))
continue
launcher.start(plan, service, i, artifacts_content)
114 changes: 114 additions & 0 deletions src/clients/zeam/zeam_launcher.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
Module for launching a Zeam client.
"""

common = import_module("../common.star")

BASE_SERVICE_NAME = "zeam"
DEFAULT_IMAGE = "ethpandaops/zeam:latest"
ENTRYPOINT = "/app/zig-out/bin/zeam"

def initialize(plan, image, index, key_artifact):
"""
Initialize a Zeam client with given image and index.

Args:
plan: The plan object to execute actions.
image: The Docker image to use for the client.
index: The index of the participant.
key_artifact: The name of the files artifact containing the node key.

Returns:
The launched service.
"""

if image == "":
image = DEFAULT_IMAGE

service_name = BASE_SERVICE_NAME + "-{}".format(index)

# Zeam uses scratch base image, so we need to run zeam directly
# We'll start it with minimal config and configure it properly in start()
config = ServiceConfig(
image = image,
# Run zeam with a sleep loop to keep container alive until properly configured
cmd = ["sleep", "infinity"],
ports = {
"quic": PortSpec(
number = common.QUIC_PORT,
transport_protocol = "UDP",
wait = None,
),
"http": PortSpec(
number = common.HTTP_PORT,
transport_protocol = "TCP",
wait = None,
),
"metrics": PortSpec(
number = common.METRICS_PORT,
transport_protocol = "TCP",
wait = None,
),
},
files = {
"/config/keys": key_artifact,
},
)
return plan.add_service(service_name, config)

def start(plan, service, node_index, artifacts_content):
"""
Start the Zeam client service with the provided genesis artifacts.

Args:
plan: The plan object to execute actions.
service: The service object to start.
node_index: The index of this node.
artifacts_content: A struct containing the genesis artifact contents.
"""

common.create_root_genesis_dir(plan, service)
common.copy_genesis_content(
plan,
service,
artifacts_content.nodes_yaml,
"/genesis/nodes.yaml",
)
common.copy_genesis_content(
plan,
service,
artifacts_content.validators_yaml,
"/genesis/validators.yaml",
)
common.copy_genesis_content(
plan,
service,
artifacts_content.config_yaml,
"/genesis/config.yaml",
)
common.copy_genesis_content(
plan,
service,
artifacts_content.validator_config_yaml,
"/genesis/validator-config.yaml",
)

# Construct the full command as a single string
cmd_parts = [
"--data-dir /data",
"--custom_genesis /genesis",
"--validator_config genesis_bootnode",
"--node-id " + service.name,
"--node-key /config/keys/node{}.key".format(node_index),
]
full_cmd = " ".join(cmd_parts)

log_file = common.get_log_file_path(service.name)
# TODO: Zeam supports logging to a file directly, use that
plan.exec(
service_name = service.name,
recipe = ExecRecipe(
command = ["nohup " + ENTRYPOINT + " " + full_cmd + " >> " + log_file + " 2>&1 &"],
),
description = "Starting {}".format(service.name),
)