Skip to content

Commit

Permalink
deploy plugins from network.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
mplsgrant committed Dec 6, 2024
1 parent c15c3ca commit 5a49d86
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 61 deletions.
30 changes: 15 additions & 15 deletions resources/plugins/simln/simln.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ def get_example_activity():
print(json.dumps(_get_example_activity()))


# Take note of how click expects us to explicitly declare command line arguments.
@simln.command()
@click.argument("activity", type=str)
@click.pass_context
def launch_activity(ctx, activity: str):
"""Deploys a SimLN Activity which is a JSON list of objects"""
try:
parsed_activity = json.loads(activity)
except json.JSONDecodeError:
log.error("Invalid JSON input for activity.")
raise click.BadArgumentUsage("Activity must be a valid JSON string.") from None
plugin_dir = ctx.obj.get(PLUGIN_DIR_TAG)
print(_launch_activity(parsed_activity, plugin_dir))


def _launch_activity(activity: list[dict], plugin_dir: str) -> str:
"""Launch a SimLN chart which includes the `activity`"""
timestamp = int(time.time())
Expand All @@ -128,21 +143,6 @@ def _launch_activity(activity: list[dict], plugin_dir: str) -> str:
raise SimLNError(f"Could not write sim.json to the init container: {name}")


# Take note of how click expects us to explicitly declare command line arguments.
@simln.command()
@click.argument("activity", type=str)
@click.pass_context
def launch_activity(ctx, activity: str):
"""Deploys a SimLN Activity which is a JSON list of objects"""
try:
parsed_activity = json.loads(activity)
except json.JSONDecodeError:
log.error("Invalid JSON input for activity.")
raise click.BadArgumentUsage("Activity must be a valid JSON string.") from None
plugin_dir = ctx.obj.get(PLUGIN_DIR_TAG)
print(_launch_activity(parsed_activity, plugin_dir))


def _generate_activity_json(activity: list[dict]) -> str:
nodes = []

Expand Down
23 changes: 20 additions & 3 deletions src/warnet/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
wait_for_ingress_controller,
wait_for_pod_ready,
)
from .process import stream_command
from .process import run_command, stream_command

HINT = "\nAre you trying to run a scenario? See `warnet run --help`"

Expand Down Expand Up @@ -115,6 +115,8 @@ def _deploy(directory, debug, namespace, to_all_users):
for p in processes:
p.join()

run_plugins(directory)

elif (directory / NAMESPACES_FILE).exists():
deploy_namespaces(directory)
else:
Expand All @@ -123,6 +125,19 @@ def _deploy(directory, debug, namespace, to_all_users):
)


def run_plugins(directory):
network_file_path = directory / NETWORK_FILE

with network_file_path.open() as f:
network_file = yaml.safe_load(f)

plugins = network_file.get("plugins") or []
for plugin_cmd in plugins:
fully_qualified_cmd = f"{network_file_path.parent}/{plugin_cmd}" # relative to network.yaml
print(fully_qualified_cmd)
print(run_command(fully_qualified_cmd))


def check_logging_required(directory: Path):
# check if node-defaults has logging or metrics enabled
default_file_path = directory / DEFAULTS_FILE
Expand All @@ -137,7 +152,8 @@ def check_logging_required(directory: Path):
network_file_path = directory / NETWORK_FILE
with network_file_path.open() as f:
network_file = yaml.safe_load(f)
nodes = network_file.get("nodes", [])

nodes = network_file.get("nodes") or []
for node in nodes:
if node.get("collectLogs", False):
return True
Expand Down Expand Up @@ -295,7 +311,8 @@ def deploy_network(directory: Path, debug: bool = False, namespace: Optional[str
queue = Queue()
processes = []

for node in network_file["nodes"]:
nodes = network_file.get("nodes") or []
for node in nodes:
p = Process(target=deploy_single_node, args=(node, directory, debug, namespace, queue))
p.start()
processes.append(p)
Expand Down
5 changes: 4 additions & 1 deletion test/data/ln/network.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,7 @@ nodes:
addnode:
- tank-0000
ln:
lnd: true
lnd: true

plugins:
- "../../../resources/plugins/simln/simln.py launch-activity '[{\"source\": \"tank-0003-ln\", \"destination\": \"tank-0005-ln\", \"interval_secs\": 1, \"amount_msat\": 2000}]'"
41 changes: 34 additions & 7 deletions test/ln_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/usr/bin/env python3

import ast
import json
import os
from pathlib import Path
from typing import Optional

from test_base import TestBase

from warnet.k8s import wait_for_pod
from warnet.process import run_command, stream_command


Expand All @@ -16,6 +18,7 @@ def __init__(self):
self.imported_network_dir = self.tmpdir / "imported_network"
self.scen_dir = Path(os.path.dirname(__file__)).parent / "resources" / "scenarios"
self.plugins_dir = Path(os.path.dirname(__file__)).parent / "resources" / "plugins"
self.simln_exec = Path("simln/simln.py")

def run_test(self):
try:
Expand Down Expand Up @@ -84,12 +87,36 @@ def get_and_pay(src, tgt):
get_and_pay(4, 6)

def run_simln(self):
self.log.info("Running activity")
activity_cmd = f"{self.plugins_dir}/simln/simln.py get-example-activity"
activity = run_command(activity_cmd).strip()
self.log.info(f"Activity: {activity}")
command = f"{self.plugins_dir}/simln/simln.py launch-activity '{activity}'"
self.log.info(run_command(command))
self.log.info("Running SimLN...")
activity_cmd = f"{self.plugins_dir}/{self.simln_exec} get-example-activity"
activity = run_command(activity_cmd)
launch_cmd = f"{self.plugins_dir}/{self.simln_exec} launch-activity '{activity}'"
pod = run_command(launch_cmd).strip()
wait_for_pod(pod)
self.log.info("Checking SimLN...")
self.wait_for_predicate(self.found_results_remotely)
self.log.info("SimLN was successful.")

def found_results_remotely(self, pod: Optional[str] = None) -> bool:
if pod is None:
pod = self.get_first_simln_pod()
self.log.info(f"Checking for results file in {pod}")
results_file = run_command(
f"{self.plugins_dir}/{self.simln_exec} sh {pod} ls /working/results"
).strip()
self.log.info(f"Results file: {results_file}")
results = run_command(
f"{self.plugins_dir}/{self.simln_exec} sh {pod} cat /working/results/{results_file}"
).strip()
self.log.info(results)
return results.find("Success") > 0

def get_first_simln_pod(self):
command = f"{self.plugins_dir}/{self.simln_exec} list-pod-names"
pod_names_literal = run_command(command)
self.log.info(f"{command}: {pod_names_literal}")
pod_names = ast.literal_eval(pod_names_literal)
return pod_names[0]


if __name__ == "__main__":
Expand Down
55 changes: 20 additions & 35 deletions test/simln_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
from typing import Optional

import pexpect
from ln_test import LNTest
from test_base import TestBase

from warnet.constants import LIGHTNING_MISSION
from warnet.k8s import download, get_mission, pod_log, wait_for_pod
from warnet.k8s import download, get_mission, wait_for_pod
from warnet.process import run_command


class SimLNTest(LNTest, TestBase):
class SimLNTest(TestBase):
def __init__(self):
super().__init__()
self.network_dir = Path(os.path.dirname(__file__)) / "data" / "ln"
Expand All @@ -27,15 +26,8 @@ def run_test(self):
try:
os.chdir(self.tmpdir)
self.init_directory()

self.import_network()
self.setup_network()
self.test_channel_policies()
self.test_payments()
self.run_simln()

self.deploy_with_plugin()
self.copy_results()
self.run_activity()
finally:
self.cleanup()

Expand All @@ -46,32 +38,20 @@ def init_directory(self):
self.sut.sendline("n")
self.sut.close()

def copy_results(self):
self.log.info("Copying results")
pod = get_mission(f"{self.simln_exec} mission")[0]
self.wait_for_gossip_sync(2)
wait_for_pod(pod.metadata.name, 60)

log_resp = pod_log(pod.metadata.name, f"{self.simln_exec} primary-container")
self.log.info(log_resp.data.decode("utf-8"))
def deploy_with_plugin(self):
self.log.info("Deploy the ln network with a SimLN plugin")
results = self.warnet(f"deploy {self.network_dir}")
self.log.info(results)
wait_for_pod(self.get_first_simln_pod())

partial_func = partial(self.found_results_remotely, pod.metadata.name)
def copy_results(self):
pod = self.get_first_simln_pod()
partial_func = partial(self.found_results_remotely, pod)
self.wait_for_predicate(partial_func)

download(pod.metadata.name, Path("/working/results"), Path("."), pod.metadata.namespace)
download(pod, Path("/working/results"), Path("."))
self.wait_for_predicate(self.found_results_locally)

def run_activity(self):
cmd = f"{self.simln_exec} get-example-activity"
self.log.info(f"Activity: {cmd}")
activity_result = run_command(cmd)
activity = json.loads(activity_result)
pod_result = run_command(f"{self.simln_exec} launch-activity '{json.dumps(activity)}'")
self.log.info(f"launched activity: {pod_result}")
partial_func = partial(self.found_results_remotely, pod_result.strip())
self.wait_for_predicate(partial_func)
self.log.info("Successfully ran activity")

def wait_for_gossip_sync(self, expected: int):
self.log.info(f"Waiting for sync (expecting {expected})...")
current = 0
Expand All @@ -88,9 +68,7 @@ def wait_for_gossip_sync(self, expected: int):

def found_results_remotely(self, pod: Optional[str] = None) -> bool:
if pod is None:
pod_names_literal = run_command(f"{self.simln_exec} list-pod-names")
pod_names = ast.literal_eval(pod_names_literal)
pod = pod_names[0]
pod = self.get_first_simln_pod()
self.log.info(f"Checking for results file in {pod}")
results_file = run_command(f"{self.simln_exec} sh {pod} ls /working/results").strip()
self.log.info(f"Results file: {results_file}")
Expand All @@ -100,6 +78,13 @@ def found_results_remotely(self, pod: Optional[str] = None) -> bool:
self.log.info(results)
return results.find("Success") > 0

def get_first_simln_pod(self):
command = f"{self.simln_exec} list-pod-names"
pod_names_literal = run_command(command)
self.log.info(f"{command}: {pod_names_literal}")
pod_names = ast.literal_eval(pod_names_literal)
return pod_names[0]

def found_results_locally(self) -> bool:
directory = "results"
self.log.info(f"Searching {directory}")
Expand Down

0 comments on commit 5a49d86

Please sign in to comment.