Back out app auto-seeding; fix report-status + PCTypesStrict bugs (manifest review)
A deep Fable review of the real manifest corpus (READ-ONLY reference) showed the manifests are an ENFORCEMENT PROGRAM, not an application inventory, and that auto-seeding the Applications catalog from entry Type + Name was wrong: - The catalog ALREADY tracks these apps from the classic-shopdb migration, with version histories (PC - DMIS, UDC x11 versions, eMX / eDNC, CLM, CSF, Oracle Database, FormTracePak). Seeding from manifest labels created DUPLICATES under different names (PC-DMIS 2016 vs PC - DMIS; eDNC (bundles NTLARS) vs eMX / eDNC; OpenText HostExplorer ShopFloor vs CSF). It also misclassified config drops (eMxInfo.txt) as apps and could never match a PC's reported ARP name. So the seed-applications command + service are removed. Properly linking manifest entries to the EXISTING catalog is a curated feature, not label-scraping. Two REAL bugs the review found are fixed and kept: - Report status (R4): every healthy cycle runs Always/no-detection scripts the engine counts as "installed", so keying self-heal off installed>0 marked the common scope selfhealed forever and made 'ok' unreachable. Status now derives from explicit per-entry self-heal flags only; the stored flag no longer infers from action=='installed'; the client kit doc reflects it. - PCTypesStrict (R5): the runtime engine has no strict handling (preinstall runner only). filters.matches_pctype now applies strict only when phase == 'preinstall'; simulate + parity thread the scope phase through; the strict test uses a preinstall scope. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -94,45 +94,6 @@ def rollback_scope(scopename, phase, versionnumber):
|
||||
return versionnumber
|
||||
|
||||
|
||||
# Entry types that install actual applications (vs File/Registry/PS1/INF config).
|
||||
INSTALLER_TYPES = {'MSI', 'EXE', 'CMD', 'BAT'}
|
||||
|
||||
|
||||
def seed_applications_from_manifests(manifests):
|
||||
"""Populate the core Applications catalog from what the manifests install.
|
||||
|
||||
For every installer entry (MSI/EXE/CMD/BAT) across the given manifests,
|
||||
create an Application (idempotent, deduped by appname) so shopdb tracks the
|
||||
apps GE-Enforce deploys. `manifests` is a list of (scopename, phase, dict).
|
||||
Returns {'created': [...], 'existing': [...]} (uncommitted).
|
||||
"""
|
||||
from shopdb.api import Application
|
||||
|
||||
created, existing, seen = [], [], set()
|
||||
for _scopename, _phase, manifest in manifests:
|
||||
for entry in manifest.get('Applications', []):
|
||||
if entry.get('Type') not in INSTALLER_TYPES:
|
||||
continue
|
||||
name = (entry.get('Name') or '').strip()
|
||||
if not name or name.lower() in seen:
|
||||
continue
|
||||
seen.add(name.lower())
|
||||
if Application.query.filter(Application.appname.ilike(name)).first():
|
||||
existing.append(name)
|
||||
continue
|
||||
description = None
|
||||
comment = entry.get('_comment')
|
||||
if comment:
|
||||
description = comment.split('.')[0].strip()[:255]
|
||||
db.session.add(Application(
|
||||
appname=name[:100],
|
||||
appdescription=description,
|
||||
installpath=(entry.get('Installer') or '')[:255] or None,
|
||||
isinstallable=True))
|
||||
created.append(name)
|
||||
return {'created': created, 'existing': existing}
|
||||
|
||||
|
||||
def record_enforcement_report(payload):
|
||||
"""Record one PC's enforcement cycle (observed state). Upserts the latest
|
||||
report per (hostname, scopename, phase) and keeps prior ones as history.
|
||||
@@ -154,10 +115,14 @@ def record_enforcement_report(payload):
|
||||
|
||||
failed = int(counts.get('failed', 0))
|
||||
installed = int(counts.get('installed', 0))
|
||||
# Derive status: any failure wins; else drift-corrected installs = selfhealed.
|
||||
# Derive status from EXPLICIT self-heal flags only, never the raw installed
|
||||
# count: every healthy cycle runs Always/no-detection scripts (asset report,
|
||||
# VNC firewall, EventSaver) that the engine counts as "installed", so keying
|
||||
# self-heal off installed>0 would mark the common scope selfhealed forever
|
||||
# and make 'ok' unreachable. A drift correction is one the client flags.
|
||||
if failed > 0:
|
||||
status = 'failed'
|
||||
elif installed > 0 or any(r.get('selfhealed') for r in results):
|
||||
elif any(r.get('selfhealed') for r in results):
|
||||
status = 'selfhealed'
|
||||
else:
|
||||
status = 'ok'
|
||||
@@ -190,8 +155,10 @@ def record_enforcement_report(payload):
|
||||
report.results.append(ManifestEnforcementResult(
|
||||
entryname=item.get('name', ''),
|
||||
action=item.get('action', ''),
|
||||
selfhealed=bool(item.get('selfhealed',
|
||||
item.get('action') == 'installed')),
|
||||
# Explicit flag only; do NOT infer from action == 'installed'
|
||||
# (Always/no-detection scripts install every cycle without being a
|
||||
# drift correction).
|
||||
selfhealed=bool(item.get('selfhealed', False)),
|
||||
exitcode=item.get('exitcode'),
|
||||
message=item.get('message')))
|
||||
db.session.add(report)
|
||||
|
||||
Reference in New Issue
Block a user