From d68c6db9abdba20c2c76704052df5538d1ddd5fa Mon Sep 17 00:00:00 2001 From: "benjamin.baker" Date: Thu, 27 Aug 2026 07:10:43 -0400 Subject: [PATCH] fix endpoints plugin CI failures Migration tests knew no endpoints head revision, so the parametrized guards raised KeyError. Register it in EXPECTED_HEAD_REVISION. EndpointState declared lastreportedat both index=True and an explicit db.Index over the same column, and the baseline migration named single-column indexes differently from what create_all builds. On a pre-existing table the name-keyed guard could not see the create_all index and added a duplicate, tripping the idempotency guard. Drop the redundant declaration and rename migration indexes to ix__ to match. endpoints was absent from the universal site profile, so the installer would not offer it and prune-schema would drop its tables at provisioning. Add it. The agent design record lived under docs/, which is stripped to the public wiki and normally absent from the tree; a lone file there un-skipped the wiki-publish guards. Move it into the plugin. --- deploy/site-profile-universal.json | 1 + .../endpoints}/DESIGN-DECISIONS-agent.md | 0 .../versions/0001_endpoints_baseline.py | 19 +++++++++++-------- plugins/endpoints/models/endpoint.py | 4 ---- tests/test_plugin_migrations.py | 3 +++ 5 files changed, 15 insertions(+), 12 deletions(-) rename {docs => plugins/endpoints}/DESIGN-DECISIONS-agent.md (100%) diff --git a/deploy/site-profile-universal.json b/deploy/site-profile-universal.json index 7308f77d..12d928bd 100644 --- a/deploy/site-profile-universal.json +++ b/deploy/site-profile-universal.json @@ -4,6 +4,7 @@ "backups", "computers", "employees", + "endpoints", "geenforce", "guides", "knowledgebase", diff --git a/docs/DESIGN-DECISIONS-agent.md b/plugins/endpoints/DESIGN-DECISIONS-agent.md similarity index 100% rename from docs/DESIGN-DECISIONS-agent.md rename to plugins/endpoints/DESIGN-DECISIONS-agent.md diff --git a/plugins/endpoints/migrations/versions/0001_endpoints_baseline.py b/plugins/endpoints/migrations/versions/0001_endpoints_baseline.py index 7e80055d..99c3ab28 100644 --- a/plugins/endpoints/migrations/versions/0001_endpoints_baseline.py +++ b/plugins/endpoints/migrations/versions/0001_endpoints_baseline.py @@ -50,7 +50,9 @@ def upgrade(): sa.PrimaryKeyConstraint('endpointstateid'), sa.UniqueConstraint('assetid'), ) - create_index_if_missing('idx_endpointstates_lastreportedat', + create_index_if_missing('ix_endpointstates_assetid', + 'endpointstates', ['assetid']) + create_index_if_missing('ix_endpointstates_lastreportedat', 'endpointstates', ['lastreportedat']) create_table_if_missing( @@ -68,11 +70,11 @@ def upgrade(): ondelete='CASCADE'), sa.PrimaryKeyConstraint('endpointeventid'), ) - create_index_if_missing('ixendpointeventsassetid', 'endpointevents', + create_index_if_missing('ix_endpointevents_assetid', 'endpointevents', ['assetid']) - create_index_if_missing('ixendpointeventseventid', 'endpointevents', + create_index_if_missing('ix_endpointevents_eventid', 'endpointevents', ['eventid']) - create_index_if_missing('ixendpointeventseventtime', 'endpointevents', + create_index_if_missing('ix_endpointevents_eventtime', 'endpointevents', ['eventtime']) # Alerting/history reads by "this endpoint's events over time", so the # composite is the index that actually gets used. @@ -83,11 +85,12 @@ def upgrade(): def downgrade(): op.drop_index('idx_endpointevents_assetevent', table_name='endpointevents') - op.drop_index('ixendpointeventseventtime', table_name='endpointevents') - op.drop_index('ixendpointeventseventid', table_name='endpointevents') - op.drop_index('ixendpointeventsassetid', table_name='endpointevents') + op.drop_index('ix_endpointevents_eventtime', table_name='endpointevents') + op.drop_index('ix_endpointevents_eventid', table_name='endpointevents') + op.drop_index('ix_endpointevents_assetid', table_name='endpointevents') op.drop_table('endpointevents') - op.drop_index('idx_endpointstates_lastreportedat', + op.drop_index('ix_endpointstates_lastreportedat', table_name='endpointstates') + op.drop_index('ix_endpointstates_assetid', table_name='endpointstates') op.drop_table('endpointstates') diff --git a/plugins/endpoints/models/endpoint.py b/plugins/endpoints/models/endpoint.py index 7dd0e654..aa0bead7 100644 --- a/plugins/endpoints/models/endpoint.py +++ b/plugins/endpoints/models/endpoint.py @@ -82,10 +82,6 @@ class EndpointState(db.Model): createdat = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) - __table_args__ = ( - db.Index('idx_endpointstates_lastreportedat', 'lastreportedat'), - ) - def __repr__(self): return f"" diff --git a/tests/test_plugin_migrations.py b/tests/test_plugin_migrations.py index 371b0fc0..dfaa8220 100644 --- a/tests/test_plugin_migrations.py +++ b/tests/test_plugin_migrations.py @@ -87,6 +87,9 @@ EXPECTED_HEAD_REVISION['tools'] = 'tools0001securesend' EXPECTED_HEAD_REVISION['notifications'] = 'notifications0005boardorder' # warranty adds the proof-of-cover document columns on top of its anchor. EXPECTED_HEAD_REVISION['warranty'] = 'warranty0002proof' +# endpoints is a NEW post-cutover plugin: its 0001 genuinely creates the +# endpointstates + endpointevents tables the core chain never owned. +EXPECTED_HEAD_REVISION['endpoints'] = 'endpoints0001baseline' # Plugins built after the cutover: their 0001 baseline really creates tables the # core chain never owned.