reckless: enable case-insensitive searching

Adds a test to validate case matching.
This commit is contained in:
Alex Myers 2023-04-12 13:14:00 -05:00 committed by ShahanaFarooqui
parent d279da551b
commit 233f05e0e2
2 changed files with 57 additions and 41 deletions

View File

@ -162,7 +162,8 @@ def test_install(node_factory):
def test_disable_enable(node_factory):
"""test search, git clone, and installation to folder."""
n = get_reckless_node(node_factory)
r = reckless([f"--network={NETWORK}", "-v", "install", "testplugpass"],
# Test case-insensitive search as well
r = reckless([f"--network={NETWORK}", "-v", "install", "testPlugPass"],
dir=n.lightning_dir)
assert r.returncode == 0
assert 'dependencies installed successfully' in r.stdout
@ -172,7 +173,7 @@ def test_disable_enable(node_factory):
plugin_path = Path(n.lightning_dir) / 'reckless/testplugpass'
print(plugin_path)
assert os.path.exists(plugin_path)
r = reckless([f"--network={NETWORK}", "-v", "disable", "testplugpass"],
r = reckless([f"--network={NETWORK}", "-v", "disable", "testPlugPass"],
dir=n.lightning_dir)
assert r.returncode == 0
n.start()
@ -180,7 +181,7 @@ def test_disable_enable(node_factory):
r = reckless([f"--network={NETWORK}", "-v", "enable", "testplugpass.py"],
dir=n.lightning_dir)
assert r.returncode == 0
assert 'testplugpass.py enabled' in r.stdout
assert 'testplugpass enabled' in r.stdout
test_plugin = {'name': str(plugin_path / 'testplugpass.py'),
'active': True, 'dynamic': True}
time.sleep(1)

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
from subprocess import Popen, PIPE, TimeoutExpired
from subprocess import Popen, PIPE, TimeoutExpired, run
import sys
import json
import os
@ -145,8 +145,8 @@ class InstInfo:
tree = json.loads(r.read().decode())
for g in entry_guesses(self.name):
for f in tree:
if f['path'] == g:
self.entry = g
if f['path'].lower() == g.lower():
self.entry = f['path']
break
if self.entry is not None:
break
@ -327,6 +327,9 @@ class InferInstall():
"""Once a plugin is installed, we may need its directory and entrypoint"""
def __init__(self, name: str):
reck_contents = os.listdir(RECKLESS_CONFIG.reckless_dir)
reck_contents_lower = {}
for f in reck_contents:
reck_contents_lower.update({f.lower(): f})
def match_name(name) -> str:
for tier in range(0, 10):
@ -344,16 +347,17 @@ class InferInstall():
return name
name = match_name(name)
if name in reck_contents:
self.dir = Path(RECKLESS_CONFIG.reckless_dir).joinpath(name)
if name.lower() in reck_contents_lower.keys():
actual_name = reck_contents_lower[name.lower()]
self.dir = Path(RECKLESS_CONFIG.reckless_dir).joinpath(actual_name)
else:
raise Exception(f"Could not find a reckless directory for {name}")
plug_dir = Path(RECKLESS_CONFIG.reckless_dir).joinpath(name)
for guess in entry_guesses(name):
plug_dir = Path(RECKLESS_CONFIG.reckless_dir).joinpath(actual_name)
for guess in entry_guesses(actual_name):
for content in plug_dir.iterdir():
if content.name == guess:
self.entry = str(content)
self.name = guess
self.name = actual_name
return
raise Exception(f'plugin entrypoint not found in {self.dir}')
@ -416,8 +420,8 @@ def _search_repo(name: str, url: str) -> Union[InstInfo, None]:
print(f"Plugin repository {api_url} unavailable")
return None
# Repo is for this plugin
if repo_name == name:
MyPlugin = InstInfo(name,
if repo_name.lower() == name.lower():
MyPlugin = InstInfo(repo_name,
f'https://github.com/{repo_user}/{repo_name}',
api_url)
if not MyPlugin.get_inst_details():
@ -425,17 +429,17 @@ def _search_repo(name: str, url: str) -> Union[InstInfo, None]:
return MyPlugin
# Repo contains multiple plugins?
for x in json.loads(r.read().decode()):
if x["name"] == name:
if x["name"].lower() == name.lower():
# Look for the rest of the install details
# These are in lightningd/plugins directly
if 'lightningd/plugins/' in x['html_url']:
MyPlugin = InstInfo(name,
MyPlugin = InstInfo(x['name'],
'https://github.com/lightningd/plugins',
x['git_url'])
MyPlugin.subdir = x['name']
# submodules from another github repo
else:
MyPlugin = InstInfo(name, x['html_url'], x['git_url'])
MyPlugin = InstInfo(x['name'], x['html_url'], x['git_url'])
# Submodule URLs are appended with /tree/<commit hash>
if MyPlugin.repo.split('/')[-2] == 'tree':
MyPlugin.commit = MyPlugin.repo.split('/')[-1]
@ -519,25 +523,24 @@ def _install_plugin(src: InstInfo) -> bool:
if pip.stdout:
logging.debug(pip.stdout.read())
return False
test = Popen([Path(plugin_path).joinpath(src.entry)], cwd=str(plugin_path),
stdout=PIPE, stderr=PIPE, text=True)
test_log = []
with test.stderr:
try:
test = run([Path(plugin_path).joinpath(src.entry)],
cwd=str(plugin_path), stdout=PIPE, stderr=PIPE,
text=True, timeout=3)
for line in test.stderr:
test_log.append(line.strip('\n'))
try:
test.wait(timeout=3)
returncode = test.returncode
except TimeoutExpired:
# Plugin assumed to be okay if still running (despite no lightningd.)
test.terminate()
# FIXME: add noexec test/warning. Maybe try chmod entrypoint.
else:
if test.returncode != 0:
logging.debug("plugin testing error:")
for line in test_log:
logging.debug(f' {line}')
print('plugin testing failed')
return False
# If the plugin is still running, it's assumed to be okay.
returncode = 0
pass
if returncode != 0:
logging.debug("plugin testing error:")
for line in test_log:
logging.debug(f' {line}')
print('plugin testing failed')
return False
# Find this cute little plugin a forever home
shutil.copytree(str(plugin_path), inst_path)
@ -551,13 +554,22 @@ def install(plugin_name: str):
assert isinstance(plugin_name, str)
src = search(plugin_name)
if src:
logging.debug(f'Retrieving {plugin_name} from {src.repo}')
logging.debug(f'Retrieving {src.name} from {src.repo}')
if not _install_plugin(src):
print('installation aborted')
sys.exit(1)
inst_path = Path(RECKLESS_CONFIG.reckless_dir) / src.name / src.entry
RECKLESS_CONFIG.enable_plugin(inst_path)
enable(plugin_name)
# Match case of the containing directory
for dirname in os.listdir(RECKLESS_CONFIG.reckless_dir):
if dirname.lower() == src.name.lower():
inst_path = Path(RECKLESS_CONFIG.reckless_dir)
inst_path = inst_path / dirname / src.entry
RECKLESS_CONFIG.enable_plugin(inst_path)
enable(src.name)
return
print(('dynamic activation failed: '
f'{src.name} not found in reckless directory'))
sys.exit(1)
def uninstall(plugin_name: str):
@ -565,10 +577,13 @@ def uninstall(plugin_name: str):
assert isinstance(plugin_name, str)
logging.debug(f'Uninstalling plugin {plugin_name}')
disable(plugin_name)
plugin_dir = Path(RECKLESS_CONFIG.reckless_dir) / plugin_name
logging.debug(f'looking for {plugin_dir}')
if remove_dir(plugin_dir):
print(f"{plugin_name} uninstalled successfully.")
inst = InferInstall(plugin_name)
if not Path(inst.entry).exists():
print(f'cannot find installed plugin at expected path {inst.entry}')
sys.exit(1)
logging.debug(f'looking for {str(Path(inst.entry).parent)}')
if remove_dir(str(Path(inst.entry).parent)):
print(f"{inst.name} uninstalled successfully.")
def search(plugin_name: str) -> InstInfo:
@ -655,7 +670,7 @@ def enable(plugin_name: str):
logging.debug(('lightningd rpc unavailable. '
'Skipping dynamic activation.'))
RECKLESS_CONFIG.enable_plugin(path)
print(f'{plugin_name} enabled')
print(f'{inst.name} enabled')
def disable(plugin_name: str):
@ -680,7 +695,7 @@ def disable(plugin_name: str):
logging.debug(('lightningd rpc unavailable. '
'Skipping dynamic deactivation.'))
RECKLESS_CONFIG.disable_plugin(path)
print(f'{plugin_name} disabled')
print(f'{inst.name} disabled')
def load_config(reckless_dir: Union[str, None] = None,