Ben Baker's installed-software collection, software delivery (run_script and
update_app), agentless adoption tool and command-queue hardening, with his
authorship preserved.
Reviewed before promotion. Fixed: the endpoints plugin tests were never collected
by pytest, the SBOM snapshot table's key would exhaust on MySQL, ingest did not
truncate two columns strict MySQL would reject, and update_app's version branch
pinned a digest for a file that route never serves. Agent packaging moved off
require_role('admin') onto a new endpoints.agentpackage permission.
Contract 0.27.0. Full suite 2231, frontend 314.
70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
"""Regression tests for the endpoint command-queue staleness reaper.
|
|
|
|
A claimed command whose agent never reports back (agent killed, uninstalled, or
|
|
offline mid-flight) would otherwise pin the at-most-one-in-flight queue forever,
|
|
so every operator action against that endpoint returns 409 "already in flight".
|
|
The enqueue path reaps such rows to STATUS_EXPIRED so the queue self-heals.
|
|
"""
|
|
|
|
from datetime import timedelta
|
|
|
|
|
|
def test_reap_stale_commands_frees_a_wedged_queue(db):
|
|
from plugins.endpoints.models import EndpointCommand
|
|
from plugins.endpoints.api.routes import _reap_stale_commands, _naive_utcnow
|
|
|
|
now = _naive_utcnow()
|
|
asset_id = 4242
|
|
|
|
stale_running = EndpointCommand(
|
|
assetid=asset_id, action='mtconnect_start',
|
|
status=EndpointCommand.STATUS_RUNNING,
|
|
createdat=now - timedelta(hours=2),
|
|
claimedat=now - timedelta(hours=2))
|
|
stale_pending = EndpointCommand(
|
|
assetid=asset_id, action='mtconnect_stop',
|
|
status=EndpointCommand.STATUS_PENDING,
|
|
createdat=now - timedelta(hours=2))
|
|
fresh_running = EndpointCommand(
|
|
assetid=asset_id + 1, action='mtconnect_start',
|
|
status=EndpointCommand.STATUS_RUNNING,
|
|
createdat=now, claimedat=now)
|
|
db.session.add_all([stale_running, stale_pending, fresh_running])
|
|
db.session.commit()
|
|
|
|
reaped = _reap_stale_commands(asset_id)
|
|
db.session.commit()
|
|
|
|
reaped_ids = {command.endpointcommandid for command in reaped}
|
|
assert stale_running.endpointcommandid in reaped_ids
|
|
assert stale_pending.endpointcommandid in reaped_ids
|
|
assert stale_running.status == EndpointCommand.STATUS_EXPIRED
|
|
assert stale_pending.status == EndpointCommand.STATUS_EXPIRED
|
|
assert stale_running.completedat is not None
|
|
assert stale_running.resultmessage
|
|
# A live, recently claimed command on another endpoint is untouched.
|
|
assert fresh_running.status == EndpointCommand.STATUS_RUNNING
|
|
|
|
|
|
def test_reap_leaves_a_recently_claimed_running_command_in_flight(db):
|
|
from plugins.endpoints.models import EndpointCommand
|
|
from plugins.endpoints.api.routes import _reap_stale_commands, _naive_utcnow
|
|
|
|
now = _naive_utcnow()
|
|
asset_id = 777
|
|
|
|
# Claimed a minute ago: a slow-but-alive command must not be reaped.
|
|
running = EndpointCommand(
|
|
assetid=asset_id, action='mtconnect_start',
|
|
status=EndpointCommand.STATUS_RUNNING,
|
|
createdat=now - timedelta(minutes=1),
|
|
claimedat=now - timedelta(minutes=1))
|
|
db.session.add(running)
|
|
db.session.commit()
|
|
|
|
reaped = _reap_stale_commands(asset_id)
|
|
db.session.commit()
|
|
|
|
assert reaped == []
|
|
assert running.status == EndpointCommand.STATUS_RUNNING
|