Add governed Files integrity operations UI
This commit is contained in:
@@ -18,6 +18,7 @@ from govoplan_files.backend.schemas import (
|
||||
FileIntegrityFindingResponse,
|
||||
FileIntegrityFindingsResponse,
|
||||
FileIntegrityScanCreateRequest,
|
||||
FileIntegrityScanRunRequest,
|
||||
FileIntegrityScanResponse,
|
||||
FileIntegrityScansResponse,
|
||||
)
|
||||
@@ -93,10 +94,17 @@ def create_scan(
|
||||
@router.post("/scans/{scan_id}/run", response_model=FileIntegrityScanResponse)
|
||||
def run_scan_batch(
|
||||
scan_id: str,
|
||||
payload: FileIntegrityScanRunRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(require_scope("files:file:admin")),
|
||||
) -> FileIntegrityScanResponse:
|
||||
scan = _scan_for_tenant(session, scan_id, principal.tenant_id)
|
||||
scan = _scan_for_tenant(
|
||||
session,
|
||||
scan_id,
|
||||
principal.tenant_id,
|
||||
for_update=True,
|
||||
)
|
||||
_assert_expected_revision(scan.revision, payload.expected_revision)
|
||||
previous_status = scan.status
|
||||
try:
|
||||
run_integrity_scan_batch(session, scan)
|
||||
@@ -117,7 +125,12 @@ def run_scan_batch(
|
||||
return _scan_response(scan)
|
||||
except (FileStorageError, StorageBackendError) as exc:
|
||||
session.rollback()
|
||||
scan = _scan_for_tenant(session, scan_id, principal.tenant_id)
|
||||
scan = _scan_for_tenant(
|
||||
session,
|
||||
scan_id,
|
||||
principal.tenant_id,
|
||||
for_update=True,
|
||||
)
|
||||
mark_integrity_scan_failed(scan, error=exc)
|
||||
audit_from_principal(
|
||||
session,
|
||||
@@ -172,7 +185,13 @@ def recheck_finding(
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(require_scope("files:file:admin")),
|
||||
) -> FileIntegrityActionResponse:
|
||||
finding = _finding_for_tenant(session, finding_id, principal.tenant_id)
|
||||
finding = _finding_for_tenant(
|
||||
session,
|
||||
finding_id,
|
||||
principal.tenant_id,
|
||||
for_update=True,
|
||||
)
|
||||
_assert_expected_revision(finding.revision, payload.expected_revision)
|
||||
try:
|
||||
result = recheck_integrity_finding(
|
||||
session,
|
||||
@@ -198,7 +217,13 @@ def cleanup_finding(
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(require_scope("files:file:admin")),
|
||||
) -> FileIntegrityActionResponse:
|
||||
finding = _finding_for_tenant(session, finding_id, principal.tenant_id)
|
||||
finding = _finding_for_tenant(
|
||||
session,
|
||||
finding_id,
|
||||
principal.tenant_id,
|
||||
for_update=True,
|
||||
)
|
||||
_assert_expected_revision(finding.revision, payload.expected_revision)
|
||||
try:
|
||||
result = cleanup_orphan_finding(
|
||||
session,
|
||||
@@ -218,8 +243,13 @@ def _scan_for_tenant(
|
||||
session: Session,
|
||||
scan_id: str,
|
||||
tenant_id: str,
|
||||
*,
|
||||
for_update: bool = False,
|
||||
) -> FileIntegrityScan:
|
||||
scan = session.get(FileIntegrityScan, scan_id)
|
||||
query = session.query(FileIntegrityScan).filter(FileIntegrityScan.id == scan_id)
|
||||
if for_update:
|
||||
query = query.populate_existing().with_for_update()
|
||||
scan = query.one_or_none()
|
||||
if scan is None or scan.tenant_id != tenant_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -232,8 +262,15 @@ def _finding_for_tenant(
|
||||
session: Session,
|
||||
finding_id: str,
|
||||
tenant_id: str,
|
||||
*,
|
||||
for_update: bool = False,
|
||||
) -> FileIntegrityFinding:
|
||||
finding = session.get(FileIntegrityFinding, finding_id)
|
||||
query = session.query(FileIntegrityFinding).filter(
|
||||
FileIntegrityFinding.id == finding_id
|
||||
)
|
||||
if for_update:
|
||||
query = query.populate_existing().with_for_update()
|
||||
finding = query.one_or_none()
|
||||
if finding is None or finding.tenant_id != tenant_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -242,6 +279,17 @@ def _finding_for_tenant(
|
||||
return finding
|
||||
|
||||
|
||||
def _assert_expected_revision(current: int, expected: int) -> None:
|
||||
if current != expected:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=(
|
||||
"The integrity record changed after it was loaded; reload before "
|
||||
"performing this action."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _audit_integrity_action(session, principal, result) -> None:
|
||||
audit_from_principal(
|
||||
session,
|
||||
@@ -271,6 +319,7 @@ def _scan_response(scan: FileIntegrityScan) -> FileIntegrityScanResponse:
|
||||
storage_backend=scan.storage_backend,
|
||||
storage_prefix=scan.storage_prefix,
|
||||
status=scan.status,
|
||||
revision=scan.revision,
|
||||
phase=scan.phase,
|
||||
verify_checksums=scan.verify_checksums,
|
||||
batch_size=scan.batch_size,
|
||||
@@ -299,6 +348,7 @@ def _finding_response(
|
||||
tenant_id=finding.tenant_id,
|
||||
kind=finding.kind,
|
||||
state=finding.state,
|
||||
revision=finding.revision,
|
||||
blob_id=finding.blob_id,
|
||||
storage_key=finding.storage_key,
|
||||
expected_size_bytes=finding.expected_size_bytes,
|
||||
|
||||
Reference in New Issue
Block a user