Skip to content
Open
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
19 changes: 13 additions & 6 deletions contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30329,12 +30329,19 @@
"additionalProperties": false,
"properties": {
"result": {
"type": "array",
"items": {
"type": "string"
},
"description": [
"Output of the requested reckless command."
"oneOf": [
{
"type": "array",
"description": [
"Output of the requested reckless command."
]
},
{
"type": "object",
"description": [
"Output of the requested reckless command."
]
}
]
},
"log": {
Expand Down
5 changes: 3 additions & 2 deletions doc/reckless.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ RETURN VALUE

On success, an object is returned, containing:

- **result** (array of strings): Output of the requested reckless command.:
- (string, optional)
- **result** (one of):
- (array): Output of the requested reckless command.
- (object): Output of the requested reckless command.:
- **log** (array of strings): Verbose log entries of the requested reckless command.:
- (string, optional)

Expand Down
19 changes: 13 additions & 6 deletions doc/schemas/reckless.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,19 @@
"additionalProperties": false,
"properties": {
"result": {
"type": "array",
"items": {
"type": "string"
},
"description": [
"Output of the requested reckless command."
"oneOf": [
{
"type": "array",
"description": [
"Output of the requested reckless command."
]
},
{
"type": "object",
"description": [
"Output of the requested reckless command."
]
}
]
},
"log": {
Expand Down
45 changes: 34 additions & 11 deletions plugins/recklessrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static struct command_result *reckless_result(struct io_conn *conn,
reckless->process_failed);
return command_finished(reckless->cmd, response);
}
const jsmntok_t *results, *result, *logs, *log;
const jsmntok_t *results, *result, *logs, *log, *conf;
size_t i;
jsmn_parser parser;
jsmntok_t *toks;
Expand Down Expand Up @@ -97,15 +97,26 @@ static struct command_result *reckless_result(struct io_conn *conn,
}

response = jsonrpc_stream_success(reckless->cmd);
json_array_start(response, "result");
results = json_get_member(reckless->stdoutbuf, toks, "result");
json_for_each_arr(i, result, results) {
json_add_string(response,
NULL,
json_strdup(reckless, reckless->stdoutbuf,
result));
conf = json_get_member(reckless->stdoutbuf, results, "requested_lightning_conf");
if (conf) {
plugin_log(plugin, LOG_DBG, "dealing with listconfigs output");
json_object_start(response, "result");
json_for_each_obj(i, result, results) {
json_add_tok(response, json_strdup(tmpctx, reckless->stdoutbuf, result), result+1, reckless->stdoutbuf);
}
json_object_end(response);

} else {
json_array_start(response, "result");
json_for_each_arr(i, result, results) {
json_add_string(response,
NULL,
json_strdup(reckless, reckless->stdoutbuf,
result));
}
json_array_end(response);
}
json_array_end(response);
json_array_start(response, "log");
logs = json_get_member(reckless->stdoutbuf, toks, "log");
json_for_each_arr(i, log, logs) {
Expand Down Expand Up @@ -211,13 +222,25 @@ static struct io_plan *stderr_conn_init(struct io_conn *conn,
return stderr_read_more(conn, reckless);
}

static bool is_single_arg_cmd(const char *command) {
if (strcmp(command, "listconfig"))
return true;
if (strcmp(command, "listavailable"))
return true;
if (strcmp(command, "listinstalled"))
return true;
return false;
}

static struct command_result *reckless_call(struct command *cmd,
const char *subcommand,
const char *target,
const char *target2)
{
if (!subcommand || !target)
return command_fail(cmd, PLUGIN_ERROR, "invalid reckless call");
if (!is_single_arg_cmd(subcommand)) {
if (!subcommand || !target)
return command_fail(cmd, PLUGIN_ERROR, "invalid reckless call");
}
char **my_call;
my_call = tal_arrz(tmpctx, char *, 0);
tal_arr_expand(&my_call, "reckless");
Expand Down Expand Up @@ -273,7 +296,7 @@ static struct command_result *json_reckless(struct command *cmd,
/* Allow check command to evaluate. */
if (!param(cmd, buf, params,
p_req("command", param_string, &command),
p_req("target/subcommand", param_string, &target),
p_opt("target/subcommand", param_string, &target),
p_opt("target", param_string, &target2),
NULL))
return command_param_failed();
Expand Down
60 changes: 60 additions & 0 deletions tests/test_reckless.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pathlib import PosixPath, Path
import socket
from pyln.testing.utils import VALGRIND, SLOW_MACHINE
import json
import pytest
import os
import re
Expand Down Expand Up @@ -170,6 +171,41 @@ def test_basic_help():
assert r.search_stdout("options:") or r.search_stdout("optional arguments:")


def test_reckless_version_listconfig(node_factory):
'''Version should be reported without loading config and should advance
with lightningd.'''
node = get_reckless_node(node_factory)
r = reckless(["-V", "-v", "--json"], dir=node.lightning_dir)
assert r.returncode == 0
json_out = ''.join(r.stdout)
with open('.version', 'r') as f:
version = f.readlines()[0].strip()
assert json.loads(json_out)['result'][0] == version
assert not r.search_stdout('config file not found')

# reckless listconfig should report the reckless version as well.
NETWORK = os.environ.get('TEST_NETWORK')
if not NETWORK:
NETWORK = 'regtest'
r = reckless(['listconfig', f'--network={NETWORK}', '--json'],
dir=node.lightning_dir)
assert r.returncode == 0
result = json.loads(''.join(r.stdout))['result']
assert result['network'] == NETWORK
assert result['reckless_dir'] == str(node.lightning_dir / 'reckless')
assert result['lightning_conf'] == str(node.lightning_dir / NETWORK / 'config')
assert result['version'] == version

# Now test via reckless-rpc plugin
node.start()
listconfig = node.rpc.reckless('listconfig')
print(listconfig)
assert listconfig['result']['lightning_dir'] == str(node.lightning_dir)
assert listconfig['result']['lightning_conf'] == str(node.lightning_dir / NETWORK / 'config')
assert listconfig['result']['network'] == NETWORK
assert listconfig['result']['version'] == version


def test_contextual_help(node_factory):
n = get_reckless_node(node_factory)
for subcmd in ['install', 'uninstall', 'search',
Expand Down Expand Up @@ -238,6 +274,18 @@ def test_install(node_factory):
assert os.path.exists(plugin_path)


def test_install_cleanup(node_factory):
"""test failed installation and post install cleanup"""
n = get_reckless_node(node_factory)
n.start()
r = reckless([f"--network={NETWORK}", "-v", "install", "testplugfail"], dir=n.lightning_dir)
assert r.returncode == 0
assert r.search_stdout('testplugfail failed to start')
r.check_stderr()
plugin_path = Path(n.lightning_dir) / 'reckless/testplugfail'
assert not os.path.exists(plugin_path)


@unittest.skipIf(VALGRIND, "virtual environment triggers memleak detection")
def test_poetry_install(node_factory):
"""test search, git clone, and installation to folder."""
Expand Down Expand Up @@ -366,3 +414,15 @@ def test_reckless_uv_install(node_factory):

assert r.search_stdout('using installer pythonuv')
r.check_stderr()


def test_reckless_available(node_factory):
"""list available plugins"""
n = get_reckless_node(node_factory)
r = reckless([f"--network={NETWORK}", "listavailable", "-v", "--json"], dir=n.lightning_dir)
assert r.returncode == 0
# All plugins in the default repo should be found and identified as installable.
assert r.search_stdout('testplugfail')
assert r.search_stdout('testplugpass')
assert r.search_stdout('testplugpyproj')
assert r.search_stdout('testpluguv')
Loading
Loading