Fix agent plugin configuration handling
This commit is contained in:
@@ -22,7 +22,7 @@ The safe default is intentionally non-mutating. Installing the UI alone never gr
|
||||
Install the wheel into NetBox's virtual environment and persist it in `/opt/netbox/local_requirements.txt`:
|
||||
|
||||
```text
|
||||
netbox-plugin-store==0.1.2
|
||||
netbox-plugin-store==0.1.3
|
||||
```
|
||||
|
||||
Add the plugin to `configuration.py`:
|
||||
|
||||
@@ -156,18 +156,23 @@ class LifecycleService:
|
||||
plugin = self.client.get_plugin(request.slug)
|
||||
ensure_not_self(plugin.package_name, plugin.import_name)
|
||||
installed = self.version_provider(plugin.package_name)
|
||||
config_editor = PluginConfigurationEditor(
|
||||
self.settings.configuration_path,
|
||||
self.settings.backup_dir,
|
||||
self.settings.backups_to_keep,
|
||||
)
|
||||
requirements_editor = RequirementsEditor(
|
||||
self.settings.requirements_path,
|
||||
self.settings.backup_dir,
|
||||
self.settings.backups_to_keep,
|
||||
)
|
||||
enabled = plugin.import_name in config_editor.enabled_plugins()
|
||||
runtime_active = self.runtime_active_provider(plugin.import_name)
|
||||
config_editor: PluginConfigurationEditor | None = None
|
||||
requirements_editor: RequirementsEditor | None = None
|
||||
if self.settings.execution_mode == "agent":
|
||||
enabled = runtime_active
|
||||
else:
|
||||
config_editor = PluginConfigurationEditor(
|
||||
self.settings.configuration_path,
|
||||
self.settings.backup_dir,
|
||||
self.settings.backups_to_keep,
|
||||
)
|
||||
requirements_editor = RequirementsEditor(
|
||||
self.settings.requirements_path,
|
||||
self.settings.backup_dir,
|
||||
self.settings.backups_to_keep,
|
||||
)
|
||||
enabled = plugin.import_name in config_editor.enabled_plugins()
|
||||
release: Release | None = None
|
||||
if request.action in {"install", "update"}:
|
||||
release = plugin.select_release(self.settings.netbox_version, request.version)
|
||||
@@ -200,10 +205,20 @@ class LifecycleService:
|
||||
)
|
||||
try:
|
||||
if self.settings.execution_mode == "agent":
|
||||
result = self._execute_agent(request, plugin, release, installed, enabled, plan, requested_by)
|
||||
result = self._execute_agent(
|
||||
request, plugin, release, installed, enabled, plan, requested_by
|
||||
)
|
||||
else:
|
||||
assert config_editor is not None and requirements_editor is not None
|
||||
result = self._execute_direct(
|
||||
request, plugin, release, installed, enabled, plan, config_editor, requirements_editor
|
||||
request,
|
||||
plugin,
|
||||
release,
|
||||
installed,
|
||||
enabled,
|
||||
plan,
|
||||
config_editor,
|
||||
requirements_editor,
|
||||
)
|
||||
except Exception as exc:
|
||||
self.repository.update_status(
|
||||
@@ -268,9 +283,19 @@ class LifecycleService:
|
||||
elif action == "update":
|
||||
plan.extend(["Update the persistent requirement pin atomically.", "Upgrade from the verified local artifact."])
|
||||
elif action == "enable":
|
||||
plan.append(f"Add {plugin.import_name} to the static PLUGINS list atomically.")
|
||||
target = (
|
||||
"agent-managed plugin list"
|
||||
if self.settings.execution_mode == "agent"
|
||||
else "static PLUGINS list"
|
||||
)
|
||||
plan.append(f"Add {plugin.import_name} to the {target} atomically.")
|
||||
elif action == "disable":
|
||||
plan.append(f"Remove {plugin.import_name} from the static PLUGINS list atomically.")
|
||||
target = (
|
||||
"agent-managed plugin list"
|
||||
if self.settings.execution_mode == "agent"
|
||||
else "static PLUGINS list"
|
||||
)
|
||||
plan.append(f"Remove {plugin.import_name} from the {target} atomically.")
|
||||
elif action == "uninstall":
|
||||
plan.extend(["Remove the persistent requirement pin atomically.", "Uninstall the distribution with pip."])
|
||||
if action == "enable" or (action == "update" and currently_enabled):
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.2"
|
||||
__version__ = "0.1.3"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "netbox-plugin-store"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
description = "A secure NetBox 4.6 plugin lifecycle client for the MrBlake Plugin Store"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -93,22 +93,25 @@ class FakeRunner:
|
||||
def runtime(root: Path, *, execution_mode="direct", allow=True):
|
||||
config = root / "configuration.py"
|
||||
config.write_text("PLUGINS = ['netbox_plugin_store']\n", encoding="utf-8")
|
||||
values = {
|
||||
"store_url": "https://store.example",
|
||||
"allowed_store_urls": ["https://store.example"],
|
||||
"allowed_artifact_urls": ["https://store.example"],
|
||||
"configuration_path": str(config),
|
||||
"requirements_path": str(root / "local_requirements.txt"),
|
||||
"manage_path": str(root / "manage.py"),
|
||||
"lock_path": str(root / "operation.lock"),
|
||||
"backup_dir": str(root / "backups"),
|
||||
"execution_mode": execution_mode,
|
||||
"allow_lifecycle_mutations": allow,
|
||||
"run_migrations": False,
|
||||
"collect_static": False,
|
||||
"allow_package_index": False,
|
||||
}
|
||||
if execution_mode == "agent":
|
||||
values["agent_socket_path"] = str(root / "agent.sock")
|
||||
return RuntimeSettings.from_mapping(
|
||||
{
|
||||
"store_url": "https://store.example",
|
||||
"allowed_store_urls": ["https://store.example"],
|
||||
"allowed_artifact_urls": ["https://store.example"],
|
||||
"configuration_path": str(config),
|
||||
"requirements_path": str(root / "local_requirements.txt"),
|
||||
"manage_path": str(root / "manage.py"),
|
||||
"lock_path": str(root / "operation.lock"),
|
||||
"backup_dir": str(root / "backups"),
|
||||
"execution_mode": execution_mode,
|
||||
"allow_lifecycle_mutations": allow,
|
||||
"run_migrations": False,
|
||||
"collect_static": False,
|
||||
"allow_package_index": False,
|
||||
},
|
||||
values,
|
||||
configuration_dir=root,
|
||||
netbox_root=root,
|
||||
base_dir=root,
|
||||
@@ -117,6 +120,27 @@ def runtime(root: Path, *, execution_mode="direct", allow=True):
|
||||
|
||||
|
||||
class LifecycleTests(unittest.TestCase):
|
||||
def test_agent_mode_does_not_parse_agent_managed_plugins_from_configuration(self):
|
||||
with tempfile.TemporaryDirectory() as temp_name:
|
||||
root = Path(temp_name)
|
||||
settings = runtime(root, execution_mode="agent")
|
||||
settings.configuration_path.write_text(
|
||||
"PLUGINS = ['netbox_plugin_store']\nPLUGINS = list(PLUGINS)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
service = LifecycleService(
|
||||
settings,
|
||||
FakeClient(catalog_plugin()),
|
||||
FakeRepository(),
|
||||
runner=FakeRunner(),
|
||||
version_provider=lambda _: "",
|
||||
runtime_active_provider=lambda _: False,
|
||||
)
|
||||
|
||||
result = service.execute(LifecycleRequest("example-plugin", "install", "1.0.0", True))
|
||||
|
||||
self.assertEqual(result.state, "dry-run")
|
||||
|
||||
def test_dry_run_has_no_download_or_subprocess(self):
|
||||
with tempfile.TemporaryDirectory() as temp_name:
|
||||
root = Path(temp_name)
|
||||
|
||||
Reference in New Issue
Block a user