Skip to content

Commit

Permalink
reckless: all command functions return objects to enable json out
Browse files Browse the repository at this point in the history
This more easily allows list output for commands accepting list
input, i.e., installing 3 plugins produces 3 outputs.
  • Loading branch information
endothermicdev committed Jul 30, 2024
1 parent e9c3f93 commit 62cdba2
Showing 1 changed file with 66 additions and 30 deletions.
96 changes: 66 additions & 30 deletions tools/reckless
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ logging.basicConfig(
handlers=[logging.StreamHandler(stream=sys.stdout)],
)

LAST_FOUND = None


class Logger:
"""Redirect logging output to a json object or stdout as appropriate."""
Expand All @@ -52,7 +54,6 @@ class Logger:
return
if self.capture:
self.json_output['log'].append(f"INFO: {to_log}")
self.json_output['result'].append(to_log)
else:
print(to_log)

Expand All @@ -74,6 +75,18 @@ class Logger:
else:
logging.error(to_log)

def add_result(self, result: Union[str, None]):
assert json.dumps(result), "result must be json serializable"
self.json_output["result"].append(result)

def reply_json(self):
"""json output to stdout with accumulated result."""
if len(log.json_output["result"]) == 1 and \
isinstance(log.json_output["result"][0], list):
# unpack sources output
log.json_output["result"] = log.json_output["result"][0]
print(json.dumps(log.json_output, indent=3))


log = Logger()

Expand Down Expand Up @@ -1267,8 +1280,10 @@ def _install_plugin(src: InstInfo) -> Union[InstInfo, None]:
return staged_src


def install(plugin_name: str):
"""downloads plugin from source repos, installs and activates plugin"""
def install(plugin_name: str) -> Union[str, None]:
"""Downloads plugin from source repos, installs and activates plugin.
Returns the location of the installed plugin or "None" in the case of
failure."""
assert isinstance(plugin_name, str)
# Specify a tag or commit to checkout by adding @<tag> to plugin name
if '@' in plugin_name:
Expand All @@ -1278,14 +1293,16 @@ def install(plugin_name: str):
name = plugin_name
commit = None
log.debug(f"Searching for {name}")
src = search(name)
if src:
if search(name):
global LAST_FOUND
src = LAST_FOUND
src.commit = commit
log.debug(f'Retrieving {src.name} from {src.source_loc}')
installed = _install_plugin(src)
LAST_FOUND = None
if not installed:
log.warning('installation aborted')
sys.exit(1)
log.warning(f'{plugin_name}: installation aborted')
return None

# Match case of the containing directory
for dirname in os.listdir(RECKLESS_CONFIG.reckless_dir):
Expand All @@ -1294,25 +1311,30 @@ def install(plugin_name: str):
inst_path = inst_path / dirname / installed.entry
RECKLESS_CONFIG.enable_plugin(inst_path)
enable(installed.name)
return
return f"{installed.source_loc}"
log.error(('dynamic activation failed: '
f'{installed.name} not found in reckless directory'))
sys.exit(1)
return None
return None


def uninstall(plugin_name: str):
"""disables plugin and deletes the plugin's reckless dir"""
def uninstall(plugin_name: str) -> str:
"""dDisables plugin and deletes the plugin's reckless dir. Returns the
status of the uninstall attempt."""
assert isinstance(plugin_name, str)
log.debug(f'Uninstalling plugin {plugin_name}')
disable(plugin_name)
inst = InferInstall(plugin_name)
if not Path(inst.entry).exists():
log.error("cannot find installed plugin at expected path"
f"{inst.entry}")
sys.exit(1)
return "uninstall failed"
log.debug(f'looking for {str(Path(inst.entry).parent)}')
if remove_dir(str(Path(inst.entry).parent)):
log.info(f"{inst.name} uninstalled successfully.")
else:
return "uninstall failed"
return "uninstalled"


def search(plugin_name: str) -> Union[InstInfo, None]:
Expand Down Expand Up @@ -1344,7 +1366,10 @@ def search(plugin_name: str) -> Union[InstInfo, None]:
log.debug(f"entry: {found.entry}")
if found.subdir:
log.debug(f'sub-directory: {found.subdir}')
return found
global LAST_FOUND
# Stashing the search result saves install() a call to _source_search.
LAST_FOUND = found
return str(found.source_loc)
log.debug("Search exhausted all sources")
return None

Expand Down Expand Up @@ -1408,12 +1433,14 @@ def enable(plugin_name: str):
log.debug(f'{inst.name} is already running')
else:
log.error(f'reckless: {inst.name} failed to start!')
raise err
log.error(err)
return None
except RPCError:
log.debug(('lightningd rpc unavailable. '
'Skipping dynamic activation.'))
RECKLESS_CONFIG.enable_plugin(path)
log.info(f'{inst.name} enabled')
return 'enabled'


def disable(plugin_name: str):
Expand All @@ -1424,7 +1451,7 @@ def disable(plugin_name: str):
path = inst.entry
if not Path(path).exists():
sys.stderr.write(f'Could not find plugin at {path}\n')
sys.exit(1)
return None
log.debug(f'deactivating {plugin_name}')
try:
lightning_cli('plugin', 'stop', path)
Expand All @@ -1433,12 +1460,14 @@ def disable(plugin_name: str):
log.debug('plugin not currently running')
else:
log.error('lightning-cli plugin stop failed')
raise err
logging.error(err)
return None
except RPCError:
log.debug(('lightningd rpc unavailable. '
'Skipping dynamic deactivation.'))
RECKLESS_CONFIG.disable_plugin(path)
log.info(f'{inst.name} disabled')
return 'disabled'


def load_config(reckless_dir: Union[str, None] = None,
Expand Down Expand Up @@ -1518,18 +1547,17 @@ def add_source(src: str):
assert isinstance(src, str)
# Is it a file?
maybe_path = os.path.realpath(src)
sources = Config(path=str(get_sources_file()),
default_text='https://github.com/lightningd/plugins')
if Path(maybe_path).exists():
if os.path.isdir(maybe_path):
default_repo = 'https://github.com/lightningd/plugins'
my_file = Config(path=str(get_sources_file()),
default_text=default_repo)
my_file.editConfigFile(src, None)
sources.editConfigFile(src, None)
elif 'github.com' in src or 'http://' in src or 'https://' in src:
my_file = Config(path=str(get_sources_file()),
default_text='https://github.com/lightningd/plugins')
my_file.editConfigFile(src, None)
sources.editConfigFile(src, None)
else:
log.warning(f'failed to add source {src}')
return None
return sources_from_file()


def remove_source(src: str):
Expand All @@ -1542,12 +1570,20 @@ def remove_source(src: str):
log.info('plugin source removed')
else:
log.warning(f'source not found: {src}')
return sources_from_file()


def list_source():
"""Provide the user with all stored source repositories."""
for src in sources_from_file():
log.info(src)
return sources_from_file()


def report_version() -> str:
"""return reckless version"""
log.info(__VERSION__)
log.add_result(__VERSION__)


class StoreIdempotent(argparse.Action):
Expand Down Expand Up @@ -1632,6 +1668,9 @@ if __name__ == '__main__':
'"reckless <cmd> -h"')
help_cmd.add_argument('targets', type=str, nargs='*')
help_cmd.set_defaults(func=help_alias)
parser.add_argument('-V', '--version',
action=StoreTrueIdempotent, const=None,
help='print version and exit')

all_parsers = [parser, install_cmd, uninstall_cmd, search_cmd, enable_cmd,
disable_cmd, list_parse, source_add, source_rem, help_cmd]
Expand All @@ -1654,8 +1693,6 @@ if __name__ == '__main__':
type=str)
p.add_argument('-v', '--verbose', action=StoreTrueIdempotent,
const=None)
p.add_argument('-V', '--version', action='store_true',
help='return reckless version and exit')
p.add_argument('-j', '--json', action=StoreTrueIdempotent,
help='output in json format')

Expand All @@ -1674,7 +1711,7 @@ if __name__ == '__main__':
SUPPORTED_NETWORKS = ['bitcoin', 'regtest', 'liquid', 'liquid-regtest',
'litecoin', 'signet', 'testnet']
if args.version:
log.info(__VERSION__)
report_version()
elif args.cmd1 is None:
parser.print_help(sys.stdout)
sys.exit(1)
Expand Down Expand Up @@ -1718,10 +1755,9 @@ if __name__ == '__main__':
args.func(args.targets)
sys.exit(0)
for target in args.targets:
args.func(target)
log.add_result(args.func(target))
elif 'func' in args:
args.func()
log.add_result(args.func())

# reply with json if requested
if log.capture:
print(json.dumps(log.json_output, indent=4))
log.reply_json()

0 comments on commit 62cdba2

Please sign in to comment.