62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import hashlib
|
|
import json
|
|
from typing import Mapping
|
|
|
|
|
|
def canonical_json(value: object) -> bytes:
|
|
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class EvidenceClosure:
|
|
evidence: Mapping[str, str]
|
|
closure_sha256: str
|
|
|
|
|
|
class ActivationEvidenceClosureBuilder:
|
|
def build(self, evidence: Mapping[str, str]) -> EvidenceClosure:
|
|
required = {
|
|
"activation_prerequisites_commit_oid",
|
|
"activation_prerequisites_tree_oid",
|
|
"artifact_sha256",
|
|
"rollback_artifact_sha256",
|
|
"validator_sha256",
|
|
"state_machine_sha256",
|
|
"branch_protection_adapter_sha256",
|
|
"rollback_configuration_sha256",
|
|
"test_evidence_sha256",
|
|
"deployment_manifest_sha256",
|
|
"dormant_unit_manifest_sha256",
|
|
"dormant_deployment_evidence_sha256",
|
|
}
|
|
if set(evidence) != required:
|
|
raise ValueError("evidence set mismatch")
|
|
for value in evidence.values():
|
|
if len(value) != 40 and len(value) != 64:
|
|
raise ValueError("invalid evidence digest")
|
|
return EvidenceClosure(
|
|
dict(evidence), hashlib.sha256(canonical_json(evidence)).hexdigest()
|
|
)
|
|
|
|
|
|
class ApprovalMirrorStatusVerifier:
|
|
def verify(
|
|
self,
|
|
expected_oid: str,
|
|
*,
|
|
gitea_oid: str,
|
|
quarantine_oid: str,
|
|
authoritative_oid: str,
|
|
controller_visible_oid: str,
|
|
fsck_connectivity: str,
|
|
) -> bool:
|
|
return (
|
|
len(expected_oid) == 40
|
|
and {gitea_oid, quarantine_oid, authoritative_oid, controller_visible_oid}
|
|
== {expected_oid}
|
|
and fsck_connectivity == "PASS"
|
|
)
|