fix(host_agent): maintenance reads the swarm through the loaded Docker plugin, not by importing its models again (#5768)
Importing plugins.docker.models by path defines its tables a second time, because the plugin manager loaded them as _steward_plugin_docker.models. Inside the app that raises InvalidRequestError, not ImportError. The cadence integration test hit it, and #5767's _swarm (used when a job is planned) carried the same import. Both now go through maintenance.docker_models(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -172,11 +172,9 @@ async def _back(session, host, job, reg, now: datetime) -> str | None:
|
|||||||
node = None
|
node = None
|
||||||
services: list = []
|
services: list = []
|
||||||
if job.node_id:
|
if job.node_id:
|
||||||
try:
|
models = maintenance.docker_models()
|
||||||
from plugins.docker.models import DockerSwarmNode, DockerSwarmService
|
if models is not None:
|
||||||
except ImportError:
|
DockerSwarmNode, DockerSwarmService = models.DockerSwarmNode, models.DockerSwarmService
|
||||||
DockerSwarmNode = DockerSwarmService = None
|
|
||||||
if DockerSwarmNode is not None:
|
|
||||||
node = (await session.execute(select(DockerSwarmNode).where(
|
node = (await session.execute(select(DockerSwarmNode).where(
|
||||||
DockerSwarmNode.node_id == job.node_id)
|
DockerSwarmNode.node_id == job.node_id)
|
||||||
.order_by(DockerSwarmNode.updated_at.desc()).limit(1))).scalar_one_or_none()
|
.order_by(DockerSwarmNode.updated_at.desc()).limit(1))).scalar_one_or_none()
|
||||||
|
|||||||
@@ -378,14 +378,29 @@ def summary(job) -> str:
|
|||||||
return f"{head}: {', '.join(parts)}" if parts else head
|
return f"{head}: {', '.join(parts)}" if parts else head
|
||||||
|
|
||||||
|
|
||||||
|
def docker_models():
|
||||||
|
"""The loaded Docker plugin's models module, or None when it isn't loaded.
|
||||||
|
Reached through the plugin manager: importing plugins.docker.models by its
|
||||||
|
path would define its tables a second time, and the app would refuse it."""
|
||||||
|
import importlib
|
||||||
|
from steward.core.plugin_manager import get_loaded_plugin_modules
|
||||||
|
loaded = get_loaded_plugin_modules().get("docker")
|
||||||
|
if loaded is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return importlib.import_module(f"{loaded.__name__}.models")
|
||||||
|
except ImportError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def _swarm(session) -> tuple:
|
async def _swarm(session) -> tuple:
|
||||||
"""({node hostname: node id}, {manager host id: its node id}) from what the
|
"""({node hostname: node id}, {manager host id: its node id}) from what the
|
||||||
Docker plugin last stored; empty when it isn't installed. A manager is a
|
Docker plugin last stored; empty when it isn't installed. A manager is a
|
||||||
host whose agent reported the swarm's nodes."""
|
host whose agent reported the swarm's nodes."""
|
||||||
try:
|
models = docker_models()
|
||||||
from plugins.docker.models import DockerSwarmNode
|
if models is None:
|
||||||
except ImportError:
|
|
||||||
return {}, {}
|
return {}, {}
|
||||||
|
DockerSwarmNode = models.DockerSwarmNode
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from steward.models.hosts import Host
|
from steward.models.hosts import Host
|
||||||
rows = (await session.execute(select(DockerSwarmNode))).scalars().all()
|
rows = (await session.execute(select(DockerSwarmNode))).scalars().all()
|
||||||
|
|||||||
Reference in New Issue
Block a user