semantics
This commit is contained in:
@@ -1,23 +1,29 @@
|
||||
# [DEF:compliance_execution_service:Module]
|
||||
# [DEF:ComplianceExecutionService:Module]
|
||||
# @COMPLEXITY: 5
|
||||
# @SEMANTICS: clean-release, compliance, execution, stages, immutable-evidence
|
||||
# @PURPOSE: Create and execute compliance runs with trusted snapshots, deterministic stages, violations and immutable report persistence.
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.repository
|
||||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.policy_resolution_service
|
||||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.stages
|
||||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.report_builder
|
||||
# @RELATION: [DEPENDS_ON] ->[RepositoryRelations]
|
||||
# @RELATION: [DEPENDS_ON] ->[PolicyResolutionService]
|
||||
# @RELATION: [DEPENDS_ON] ->[ComplianceStages]
|
||||
# @RELATION: [DEPENDS_ON] ->[ReportBuilder]
|
||||
# @INVARIANT: A run binds to exactly one candidate/manifest/policy/registry snapshot set.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Iterable, List, Optional
|
||||
from typing import Any, Iterable, List, Optional, cast
|
||||
from uuid import uuid4
|
||||
|
||||
from ...core.logger import belief_scope, logger
|
||||
from ...models.clean_release import ComplianceReport, ComplianceRun, ComplianceStageRun, ComplianceViolation, DistributionManifest
|
||||
from ...models.clean_release import (
|
||||
ComplianceReport,
|
||||
ComplianceRun,
|
||||
ComplianceStageRun,
|
||||
ComplianceViolation,
|
||||
DistributionManifest,
|
||||
)
|
||||
from .audit_service import audit_check_run, audit_report, audit_violation
|
||||
from .enums import ComplianceDecision, RunStatus
|
||||
from .exceptions import ComplianceRunError, PolicyResolutionError
|
||||
@@ -28,6 +34,9 @@ from .stages import build_default_stages, derive_final_status
|
||||
from .stages.base import ComplianceStage, ComplianceStageContext, build_stage_run_record
|
||||
|
||||
|
||||
belief_logger = cast(Any, logger)
|
||||
|
||||
|
||||
# [DEF:ComplianceExecutionResult:Class]
|
||||
# @PURPOSE: Return envelope for compliance execution with run/report and persisted stage artifacts.
|
||||
@dataclass
|
||||
@@ -36,6 +45,8 @@ class ComplianceExecutionResult:
|
||||
report: Optional[ComplianceReport]
|
||||
stage_runs: List[ComplianceStageRun]
|
||||
violations: List[ComplianceViolation]
|
||||
|
||||
|
||||
# [/DEF:ComplianceExecutionResult:Class]
|
||||
|
||||
|
||||
@@ -45,6 +56,7 @@ class ComplianceExecutionResult:
|
||||
# @POST: run state, stage records, violations and optional report are persisted consistently.
|
||||
class ComplianceExecutionService:
|
||||
TASK_PLUGIN_ID = "clean-release-compliance"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -61,28 +73,36 @@ class ComplianceExecutionService:
|
||||
# @PURPOSE: Resolve explicit manifest or fallback to latest candidate manifest.
|
||||
# @PRE: candidate exists.
|
||||
# @POST: Returns manifest snapshot or raises ComplianceRunError.
|
||||
def _resolve_manifest(self, candidate_id: str, manifest_id: Optional[str]) -> DistributionManifest:
|
||||
def _resolve_manifest(
|
||||
self, candidate_id: str, manifest_id: Optional[str]
|
||||
) -> DistributionManifest:
|
||||
with belief_scope("ComplianceExecutionService._resolve_manifest"):
|
||||
if manifest_id:
|
||||
manifest = self.repository.get_manifest(manifest_id)
|
||||
manifest_id_value = cast(Optional[str], manifest_id)
|
||||
if manifest_id_value is not None and manifest_id_value != "":
|
||||
manifest = self.repository.get_manifest(manifest_id_value)
|
||||
if manifest is None:
|
||||
raise ComplianceRunError(f"manifest '{manifest_id}' not found")
|
||||
if manifest.candidate_id != candidate_id:
|
||||
raise ComplianceRunError(
|
||||
f"manifest '{manifest_id_value}' not found"
|
||||
)
|
||||
if str(getattr(manifest, "candidate_id")) != candidate_id:
|
||||
raise ComplianceRunError("manifest does not belong to candidate")
|
||||
return manifest
|
||||
|
||||
manifests = self.repository.get_manifests_by_candidate(candidate_id)
|
||||
if not manifests:
|
||||
raise ComplianceRunError(f"candidate '{candidate_id}' has no manifest")
|
||||
return sorted(manifests, key=lambda item: item.manifest_version, reverse=True)[0]
|
||||
return sorted(
|
||||
manifests, key=lambda item: item.manifest_version, reverse=True
|
||||
)[0]
|
||||
|
||||
# [/DEF:_resolve_manifest:Function]
|
||||
|
||||
# [DEF:_persist_stage_run:Function]
|
||||
# @PURPOSE: Persist stage run if repository supports stage records.
|
||||
# @POST: Stage run is persisted when adapter is available, otherwise no-op.
|
||||
def _persist_stage_run(self, stage_run: ComplianceStageRun) -> None:
|
||||
if hasattr(self.repository, "save_stage_run"):
|
||||
self.repository.save_stage_run(stage_run)
|
||||
self.repository.save_stage_run(stage_run)
|
||||
|
||||
# [/DEF:_persist_stage_run:Function]
|
||||
|
||||
# [DEF:_persist_violations:Function]
|
||||
@@ -91,6 +111,7 @@ class ComplianceExecutionService:
|
||||
def _persist_violations(self, violations: List[ComplianceViolation]) -> None:
|
||||
for violation in violations:
|
||||
self.repository.save_violation(violation)
|
||||
|
||||
# [/DEF:_persist_violations:Function]
|
||||
|
||||
# [DEF:execute_run:Function]
|
||||
@@ -105,7 +126,9 @@ class ComplianceExecutionService:
|
||||
manifest_id: Optional[str] = None,
|
||||
) -> ComplianceExecutionResult:
|
||||
with belief_scope("ComplianceExecutionService.execute_run"):
|
||||
logger.reason(f"Starting compliance execution candidate_id={candidate_id}")
|
||||
belief_logger.reason(
|
||||
f"Starting compliance execution candidate_id={candidate_id}"
|
||||
)
|
||||
|
||||
candidate = self.repository.get_candidate(candidate_id)
|
||||
if candidate is None:
|
||||
@@ -124,10 +147,10 @@ class ComplianceExecutionService:
|
||||
run = ComplianceRun(
|
||||
id=f"run-{uuid4()}",
|
||||
candidate_id=candidate_id,
|
||||
manifest_id=manifest.id,
|
||||
manifest_digest=manifest.manifest_digest,
|
||||
policy_snapshot_id=policy_snapshot.id,
|
||||
registry_snapshot_id=registry_snapshot.id,
|
||||
manifest_id=str(getattr(manifest, "id")),
|
||||
manifest_digest=str(getattr(manifest, "manifest_digest")),
|
||||
policy_snapshot_id=str(getattr(policy_snapshot, "id")),
|
||||
registry_snapshot_id=str(getattr(registry_snapshot, "id")),
|
||||
requested_by=requested_by,
|
||||
requested_at=datetime.now(timezone.utc),
|
||||
started_at=datetime.now(timezone.utc),
|
||||
@@ -154,7 +177,7 @@ class ComplianceExecutionService:
|
||||
finished = datetime.now(timezone.utc)
|
||||
|
||||
stage_run = build_stage_run_record(
|
||||
run_id=run.id,
|
||||
run_id=str(getattr(run, "id")),
|
||||
stage_name=stage.stage_name,
|
||||
result=result,
|
||||
started_at=started,
|
||||
@@ -167,23 +190,27 @@ class ComplianceExecutionService:
|
||||
self._persist_violations(result.violations)
|
||||
violations.extend(result.violations)
|
||||
|
||||
run.final_status = derive_final_status(stage_runs).value
|
||||
run.status = RunStatus.SUCCEEDED.value
|
||||
run.finished_at = datetime.now(timezone.utc)
|
||||
setattr(run, "final_status", derive_final_status(stage_runs).value)
|
||||
setattr(run, "status", RunStatus.SUCCEEDED.value)
|
||||
setattr(run, "finished_at", datetime.now(timezone.utc))
|
||||
self.repository.save_check_run(run)
|
||||
|
||||
report = self.report_builder.build_report_payload(run, violations)
|
||||
report = self.report_builder.persist_report(report)
|
||||
run.report_id = report.id
|
||||
setattr(run, "report_id", str(getattr(report, "id")))
|
||||
self.repository.save_check_run(run)
|
||||
logger.reflect(f"[REFLECT] Compliance run completed run_id={run.id} final_status={run.final_status}")
|
||||
belief_logger.reflect(
|
||||
f"[REFLECT] Compliance run completed run_id={getattr(run, 'id')} final_status={getattr(run, 'final_status', None)}"
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
run.status = RunStatus.FAILED.value
|
||||
run.final_status = ComplianceDecision.ERROR.value
|
||||
run.failure_reason = str(exc)
|
||||
run.finished_at = datetime.now(timezone.utc)
|
||||
setattr(run, "status", RunStatus.FAILED.value)
|
||||
setattr(run, "final_status", ComplianceDecision.ERROR.value)
|
||||
setattr(run, "failure_reason", str(exc))
|
||||
setattr(run, "finished_at", datetime.now(timezone.utc))
|
||||
self.repository.save_check_run(run)
|
||||
logger.explore(f"[EXPLORE] Compliance run failed run_id={run.id}: {exc}")
|
||||
belief_logger.explore(
|
||||
f"[EXPLORE] Compliance run failed run_id={getattr(run, 'id')}: {exc}"
|
||||
)
|
||||
|
||||
return ComplianceExecutionResult(
|
||||
run=run,
|
||||
@@ -191,7 +218,10 @@ class ComplianceExecutionService:
|
||||
stage_runs=stage_runs,
|
||||
violations=violations,
|
||||
)
|
||||
|
||||
# [/DEF:execute_run:Function]
|
||||
|
||||
|
||||
# [/DEF:ComplianceExecutionService:Class]
|
||||
|
||||
# [/DEF:backend.src.services.clean_release.compliance_execution_service:Module]
|
||||
# [/DEF:ComplianceExecutionService:Module]
|
||||
|
||||
Reference in New Issue
Block a user