Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,629 @@
|
||||
"""Atomic AppOps ownership commit for the one RepoPulse adoption.
|
||||
|
||||
This is a database-only protocol step. The caller must authenticate the live
|
||||
controller receipt before supplying the fixed registration evidence. No route,
|
||||
controller client, secret writer, or generic enrollment orchestration is used.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import or_, update
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from ..core.time import utc_now
|
||||
from ..db.models import AppSetting, Enrollment, Policy, RepoPulseAdoptionJournal
|
||||
from ..schemas.enrollment import EnrollmentPlanV1, EnrollmentTrustConfigV1
|
||||
from . import repopulse_adoption_journal as journal
|
||||
from . import repopulse_legacy_adoption_protocol as protocol
|
||||
from .enrollment_profiles import PROFILE_VERSION
|
||||
from .secret_store import has_secret
|
||||
|
||||
_SLOT = "repopulse"
|
||||
_TRUST_SETTING_KEY = "deployment_enrollment:trust"
|
||||
_CONTROLLER_IDENTITY = "unraid-controller"
|
||||
_TARGET_SECRET_REF = "ENROLL_REPOPULSE_TARGET_HMAC"
|
||||
_CREATED_BY = "repopulse-legacy-adoption/v1"
|
||||
_APPLY_POLICY_ID = "enrollment-repopulse-deployment-apply"
|
||||
_ROLLBACK_POLICY_ID = "enrollment-repopulse-deployment-rollback"
|
||||
_POLICY_REASON = "Enrollment-generated exact-target deployment policy."
|
||||
|
||||
|
||||
class RepoPulseOwnershipError(ValueError):
|
||||
"""Sanitized fixed ownership precondition or persistence error."""
|
||||
|
||||
def __init__(self, code: str):
|
||||
super().__init__(code)
|
||||
self.code = code
|
||||
|
||||
|
||||
def _need(condition: bool, code: str) -> None:
|
||||
if not condition:
|
||||
raise RepoPulseOwnershipError(code)
|
||||
|
||||
|
||||
def _canonical_plan(plan: EnrollmentPlanV1) -> tuple[dict[str, Any], str]:
|
||||
payload = plan.model_dump(mode="json")
|
||||
encoded = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
||||
return payload, hashlib.sha256(encoded).hexdigest()
|
||||
|
||||
|
||||
def _fixed_plan(
|
||||
session: Session, intent: dict[str, Any]
|
||||
) -> tuple[EnrollmentPlanV1, dict[str, Any], str]:
|
||||
preflight = intent["evidence"].get("preflight")
|
||||
gates = intent["evidence"].get("targetGates")
|
||||
registration = intent["evidence"].get("controllerRegistration")
|
||||
_need(
|
||||
isinstance(preflight, dict) and isinstance(gates, dict),
|
||||
"adoption_prerequisite_missing",
|
||||
)
|
||||
_need(isinstance(registration, dict), "controller_registration_missing")
|
||||
|
||||
trust_row = session.get(AppSetting, _TRUST_SETTING_KEY)
|
||||
_need(
|
||||
trust_row is not None and trust_row.is_secret is False,
|
||||
"enrollment_trust_unavailable",
|
||||
)
|
||||
try:
|
||||
trust = EnrollmentTrustConfigV1.model_validate(trust_row.value_json)
|
||||
except (TypeError, ValueError):
|
||||
raise RepoPulseOwnershipError("enrollment_trust_invalid") from None
|
||||
_need(
|
||||
trust.controller_identity == _CONTROLLER_IDENTITY,
|
||||
"controller_identity_mismatch",
|
||||
)
|
||||
_need(
|
||||
"repopulse-authenticated" in trust.autonomous_profiles
|
||||
and "itworx-mcp-hub-npm" in trust.approved_networks
|
||||
and "example.com" in trust.allowed_domain_suffixes
|
||||
and trust.repopulse_proxy_access_list_id is not None,
|
||||
"repopulse_trust_boundary_mismatch",
|
||||
)
|
||||
|
||||
revision = preflight.get("main_revision")
|
||||
_need(
|
||||
isinstance(revision, str) and len(revision) == 40, "preflight_revision_invalid"
|
||||
)
|
||||
try:
|
||||
plan = EnrollmentPlanV1.model_validate(
|
||||
{
|
||||
"application_id": protocol.APPLICATION_ID,
|
||||
"display_name": "RepoPulse",
|
||||
"environment": "production",
|
||||
"repository": "Jens/RepoPulse",
|
||||
"revision": revision,
|
||||
"profile": "repopulse-authenticated",
|
||||
"target": {
|
||||
"target_identity": _CONTROLLER_IDENTITY,
|
||||
"container_name": "repopulse",
|
||||
},
|
||||
"storage": {"runtime_uid": 10001, "runtime_gid": 10001},
|
||||
"network": {
|
||||
"docker_network": "itworx-mcp-hub-npm",
|
||||
"internal_port": 8000,
|
||||
},
|
||||
"artifact": {
|
||||
"release_version": "1.5.0",
|
||||
"image_repository": "repopulse-managed",
|
||||
"image_tag": "production",
|
||||
"health_path": "/health",
|
||||
},
|
||||
"exposure": {
|
||||
"hostname": "repopulse.example.com",
|
||||
"upstream_service": "repopulse",
|
||||
},
|
||||
"secrets": {"additional_secret_refs": ["REPOPULSE_GITEA_TOKEN"]},
|
||||
}
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
raise RepoPulseOwnershipError("fixed_repopulse_plan_invalid") from None
|
||||
|
||||
payload, plan_sha256 = _canonical_plan(plan)
|
||||
_need(
|
||||
gates.get("profileId") == "repopulse-authenticated"
|
||||
and gates.get("profileVersion") == PROFILE_VERSION
|
||||
and gates.get("planSha256") == plan_sha256
|
||||
and registration.get("planSha256") == plan_sha256,
|
||||
"fixed_repopulse_plan_digest_mismatch",
|
||||
)
|
||||
expected_enrollment_id = f"enr-{intent['intentId'][4:]}"
|
||||
_need(
|
||||
registration.get("enrollmentId") == expected_enrollment_id,
|
||||
"fixed_enrollment_id_mismatch",
|
||||
)
|
||||
return plan, payload, plan_sha256
|
||||
|
||||
|
||||
def _expected_provenance(
|
||||
intent: dict[str, Any], registration: dict[str, Any], plan_sha256: str
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"createdBy": _CREATED_BY,
|
||||
"adoptionIntentId": intent["intentId"],
|
||||
"profileVersion": PROFILE_VERSION,
|
||||
"policyTemplateVersion": "exact-target/v1",
|
||||
"sourceRevision": intent["evidence"]["preflight"]["main_revision"],
|
||||
"planSha256": plan_sha256,
|
||||
"controllerReceiptSha256": registration["receiptSha256"],
|
||||
"deploymentReceiptSha256": registration["deploymentReceiptSha256"],
|
||||
}
|
||||
|
||||
|
||||
def _expected_resources(registration: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
"appdataPath": "/srv/example/appdata/repopulse",
|
||||
"dataPath": "/srv/example/appdata/repopulse/data",
|
||||
"network": "itworx-mcp-hub-npm",
|
||||
"hostname": "repopulse.example.com",
|
||||
"controllerReceiptSha256": registration["receiptSha256"],
|
||||
"deploymentReceiptSha256": registration["deploymentReceiptSha256"],
|
||||
}
|
||||
|
||||
|
||||
def _expected_policy_rows(
|
||||
intent: dict[str, Any], registration: dict[str, Any], plan_sha256: str
|
||||
) -> tuple[Policy, Policy]:
|
||||
provenance = _expected_provenance(intent, registration, plan_sha256)
|
||||
return (
|
||||
Policy(
|
||||
id=_APPLY_POLICY_ID,
|
||||
action="deployment.apply",
|
||||
target=protocol.APPLICATION_ID,
|
||||
voice="blocked",
|
||||
manual="allowed",
|
||||
risk="high",
|
||||
reason=_POLICY_REASON,
|
||||
provenance_json=provenance,
|
||||
),
|
||||
Policy(
|
||||
id=_ROLLBACK_POLICY_ID,
|
||||
action="deployment.rollback",
|
||||
target=protocol.APPLICATION_ID,
|
||||
voice="blocked",
|
||||
manual="allowed",
|
||||
risk="high",
|
||||
reason=_POLICY_REASON,
|
||||
provenance_json=provenance,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _policy_matches(actual: Policy | None, expected: Policy) -> bool:
|
||||
return bool(
|
||||
actual is not None
|
||||
and actual.id == expected.id
|
||||
and actual.action == expected.action
|
||||
and actual.target == expected.target
|
||||
and actual.voice == expected.voice
|
||||
and actual.manual == expected.manual
|
||||
and actual.risk == expected.risk
|
||||
and actual.reason == expected.reason
|
||||
and actual.provenance_json == expected.provenance_json
|
||||
)
|
||||
|
||||
|
||||
def _expected_enrollment(
|
||||
intent: dict[str, Any],
|
||||
registration: dict[str, Any],
|
||||
plan: EnrollmentPlanV1,
|
||||
plan_json: dict[str, Any],
|
||||
plan_sha256: str,
|
||||
) -> Enrollment:
|
||||
return Enrollment(
|
||||
id=registration["enrollmentId"],
|
||||
application_id=protocol.APPLICATION_ID,
|
||||
repository=protocol.REPOSITORY,
|
||||
revision=plan.revision,
|
||||
profile="repopulse-authenticated",
|
||||
controller_identity=_CONTROLLER_IDENTITY,
|
||||
plan_sha256=plan_sha256,
|
||||
status="ready",
|
||||
target_secret_ref=_TARGET_SECRET_REF,
|
||||
oidc_secret_ref=None,
|
||||
plan_json=plan_json,
|
||||
resources_json=_expected_resources(registration),
|
||||
provenance_json=_expected_provenance(intent, registration, plan_sha256),
|
||||
)
|
||||
|
||||
|
||||
def _enrollment_matches(actual: Enrollment | None, expected: Enrollment) -> bool:
|
||||
return bool(
|
||||
actual is not None
|
||||
and actual.id == expected.id
|
||||
and actual.application_id == expected.application_id
|
||||
and actual.repository == expected.repository
|
||||
and actual.revision == expected.revision
|
||||
and actual.profile == expected.profile
|
||||
and actual.controller_identity == expected.controller_identity
|
||||
and actual.plan_sha256 == expected.plan_sha256
|
||||
and actual.status == expected.status
|
||||
and actual.target_secret_ref == expected.target_secret_ref
|
||||
and actual.oidc_secret_ref is None
|
||||
and actual.plan_json == expected.plan_json
|
||||
and actual.resources_json == expected.resources_json
|
||||
and actual.provenance_json == expected.provenance_json
|
||||
and actual.last_verified_at is not None
|
||||
and actual.completed_at is not None
|
||||
)
|
||||
|
||||
|
||||
def _verify_owned_rows(
|
||||
session: Session,
|
||||
intent: dict[str, Any],
|
||||
registration: dict[str, Any],
|
||||
plan: EnrollmentPlanV1,
|
||||
plan_json: dict[str, Any],
|
||||
plan_sha256: str,
|
||||
) -> tuple[Enrollment, tuple[Policy, Policy]]:
|
||||
expected_enrollment = _expected_enrollment(
|
||||
intent, registration, plan, plan_json, plan_sha256
|
||||
)
|
||||
matching_enrollments = session.exec(
|
||||
select(Enrollment).where(
|
||||
or_(
|
||||
Enrollment.application_id == protocol.APPLICATION_ID,
|
||||
Enrollment.repository == protocol.REPOSITORY,
|
||||
)
|
||||
)
|
||||
).all()
|
||||
_need(
|
||||
len(matching_enrollments) == 1
|
||||
and _enrollment_matches(matching_enrollments[0], expected_enrollment),
|
||||
"ownership_enrollment_readback_mismatch",
|
||||
)
|
||||
expected_policies = _expected_policy_rows(intent, registration, plan_sha256)
|
||||
_need(
|
||||
all(
|
||||
_policy_matches(session.get(Policy, expected.id), expected)
|
||||
for expected in expected_policies
|
||||
),
|
||||
"ownership_policy_readback_mismatch",
|
||||
)
|
||||
_need(
|
||||
has_secret(session, _TARGET_SECRET_REF), "target_secret_reference_unavailable"
|
||||
)
|
||||
return matching_enrollments[0], expected_policies
|
||||
|
||||
|
||||
def _verify_live_registration(
|
||||
intent: dict[str, Any], live_registration_evidence: object
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
if intent.get("status") == "completed":
|
||||
checked = protocol.validate_controller_registration_evidence(
|
||||
intent, live_registration_evidence
|
||||
)
|
||||
replayed_registration = checked
|
||||
else:
|
||||
replayed = protocol.advance_intent(
|
||||
intent, "controller_registration", live_registration_evidence
|
||||
)
|
||||
replayed_registration = replayed["evidence"].get("controllerRegistration")
|
||||
except protocol.AdoptionProtocolError as error:
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
_need(
|
||||
replayed_registration == intent["evidence"].get("controllerRegistration"),
|
||||
"idempotency_conflict",
|
||||
)
|
||||
return replayed_registration
|
||||
|
||||
|
||||
def _ownership_evidence(
|
||||
registration: dict[str, Any], plan_sha256: str
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"repository": protocol.REPOSITORY,
|
||||
"applicationId": protocol.APPLICATION_ID,
|
||||
"enrollmentId": registration["enrollmentId"],
|
||||
"profileId": registration["profileId"],
|
||||
"planSha256": plan_sha256,
|
||||
"controllerReceiptSha256": registration["receiptSha256"],
|
||||
"enrollmentPersisted": True,
|
||||
"exactApplyPolicyPersisted": True,
|
||||
"exactRollbackPolicyPersisted": True,
|
||||
"secretReferenceReady": True,
|
||||
}
|
||||
|
||||
|
||||
def _advance_or_verify_ownership(
|
||||
intent: dict[str, Any], evidence: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
if intent.get("status") == "completed":
|
||||
checked = protocol.validate_appops_ownership_evidence(intent, evidence)
|
||||
_need(
|
||||
checked == intent["evidence"].get("appopsOwnership"),
|
||||
"idempotency_conflict",
|
||||
)
|
||||
return intent
|
||||
return protocol.advance_intent(intent, "appops_ownership", evidence)
|
||||
except protocol.AdoptionProtocolError as error:
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
|
||||
|
||||
def _write_journal_update(
|
||||
session: Session,
|
||||
row: RepoPulseAdoptionJournal,
|
||||
current: dict[str, Any],
|
||||
updated: dict[str, Any],
|
||||
expected_revision: int,
|
||||
) -> int:
|
||||
result = session.execute(
|
||||
update(RepoPulseAdoptionJournal)
|
||||
.where(
|
||||
RepoPulseAdoptionJournal.slot == _SLOT,
|
||||
RepoPulseAdoptionJournal.revision == expected_revision,
|
||||
RepoPulseAdoptionJournal.intent_id == current["intentId"],
|
||||
)
|
||||
.values(
|
||||
idempotency_sha256=updated["idempotencySha256"],
|
||||
preflight_sha256=updated["preflightSha256"],
|
||||
phase=updated["phase"],
|
||||
status=updated["status"],
|
||||
intent_json=updated,
|
||||
revision=expected_revision + 1,
|
||||
updated_at=utc_now(),
|
||||
)
|
||||
.execution_options(synchronize_session=False)
|
||||
)
|
||||
_need(result.rowcount == 1, "journal_revision_conflict")
|
||||
return expected_revision + 1
|
||||
|
||||
|
||||
def _readback_owned(
|
||||
session: Session, registration: dict[str, Any]
|
||||
) -> dict[str, Any] | None:
|
||||
session.rollback()
|
||||
session.expire_all()
|
||||
try:
|
||||
row, intent = journal._load(session)
|
||||
if row is None or intent is None:
|
||||
return None
|
||||
live = _verify_live_registration(intent, registration)
|
||||
plan, plan_json, plan_sha256 = _fixed_plan(session, intent)
|
||||
_verify_owned_rows(session, intent, live, plan, plan_json, plan_sha256)
|
||||
_advance_or_verify_ownership(intent, _ownership_evidence(live, plan_sha256))
|
||||
if intent["phase"] not in {"appops_owned", "completed"}:
|
||||
return None
|
||||
if intent["phase"] == "completed":
|
||||
_assert_completion_binding(intent, live, registration)
|
||||
return journal.get_repopulse_adoption_status(session)
|
||||
except (
|
||||
RepoPulseOwnershipError,
|
||||
journal.RepoPulseAdoptionJournalError,
|
||||
protocol.AdoptionProtocolError,
|
||||
SQLAlchemyError,
|
||||
):
|
||||
session.rollback()
|
||||
return None
|
||||
|
||||
|
||||
def _record_uncertain_commit(
|
||||
session: Session, expected_revision: int, receipt_sha256: str
|
||||
) -> None:
|
||||
session.rollback()
|
||||
session.expire_all()
|
||||
try:
|
||||
row, current = journal._load(session)
|
||||
if (
|
||||
row is None
|
||||
or current is None
|
||||
or row.revision != expected_revision
|
||||
or current.get("phase") != "target_registered"
|
||||
or current.get("status") != "in_progress"
|
||||
):
|
||||
return
|
||||
journal.mark_repopulse_adoption_recovery(
|
||||
session,
|
||||
"appops_commit_uncertain",
|
||||
{"controllerReceiptSha256": receipt_sha256},
|
||||
expected_revision=expected_revision,
|
||||
)
|
||||
except (
|
||||
RepoPulseOwnershipError,
|
||||
journal.RepoPulseAdoptionJournalError,
|
||||
protocol.AdoptionProtocolError,
|
||||
SQLAlchemyError,
|
||||
):
|
||||
session.rollback()
|
||||
|
||||
|
||||
def _assert_completion_binding(
|
||||
intent: dict[str, Any],
|
||||
registration: dict[str, Any],
|
||||
live_registration: dict[str, Any],
|
||||
) -> None:
|
||||
stored = intent.get("evidence", {}).get("completion")
|
||||
_need(isinstance(stored, dict), "completion_evidence_missing")
|
||||
expected = {
|
||||
"intentId": intent["intentId"],
|
||||
"repository": protocol.REPOSITORY,
|
||||
"applicationId": protocol.APPLICATION_ID,
|
||||
"enrollmentId": registration["enrollmentId"],
|
||||
"controllerReceiptSha256": registration["receiptSha256"],
|
||||
"enrollmentStillPersisted": True,
|
||||
"exactApplyPolicyStillPersisted": True,
|
||||
"exactRollbackPolicyStillPersisted": True,
|
||||
"controllerReceiptStillMatches": (
|
||||
live_registration == registration
|
||||
and live_registration["receiptSha256"] == registration["receiptSha256"]
|
||||
),
|
||||
"legacyVolumeRetained": live_registration["legacyVolumeRetained"],
|
||||
}
|
||||
_need(stored == expected, "completion_binding_mismatch")
|
||||
|
||||
|
||||
def commit_repopulse_adoption_ownership(
|
||||
session: Session,
|
||||
authenticated_controller_registration: object,
|
||||
*,
|
||||
expected_revision: int,
|
||||
) -> dict[str, Any]:
|
||||
"""Atomically install fixed enrollment/policies and advance to `appops_owned`.
|
||||
|
||||
The caller is responsible for authenticating the live controller response.
|
||||
The protocol validator then binds every field to the persisted fixed intent.
|
||||
Exact replay reads back the DB rows and validates the fresh receipt again.
|
||||
"""
|
||||
try:
|
||||
row, current = journal._load(session)
|
||||
_need(row is not None and current is not None, "adoption_intent_not_reserved")
|
||||
live = _verify_live_registration(current, authenticated_controller_registration)
|
||||
plan, plan_json, plan_sha256 = _fixed_plan(session, current)
|
||||
_need(
|
||||
has_secret(session, _TARGET_SECRET_REF),
|
||||
"target_secret_reference_unavailable",
|
||||
)
|
||||
|
||||
if current["phase"] in {"appops_owned", "completed"}:
|
||||
_verify_owned_rows(session, current, live, plan, plan_json, plan_sha256)
|
||||
_advance_or_verify_ownership(
|
||||
current, _ownership_evidence(live, plan_sha256)
|
||||
)
|
||||
if current["phase"] == "completed":
|
||||
_assert_completion_binding(current, live, live)
|
||||
return journal.get_repopulse_adoption_status(session)
|
||||
|
||||
_need(
|
||||
current["phase"] == "target_registered"
|
||||
and current["status"] == "in_progress",
|
||||
"adoption_not_ready_for_ownership",
|
||||
)
|
||||
_need(
|
||||
isinstance(expected_revision, int)
|
||||
and not isinstance(expected_revision, bool)
|
||||
and expected_revision == row.revision,
|
||||
"journal_revision_conflict",
|
||||
)
|
||||
|
||||
conflicting_enrollments = session.exec(
|
||||
select(Enrollment).where(
|
||||
or_(
|
||||
Enrollment.application_id == protocol.APPLICATION_ID,
|
||||
Enrollment.repository == protocol.REPOSITORY,
|
||||
)
|
||||
)
|
||||
).all()
|
||||
_need(not conflicting_enrollments, "ownership_resource_conflict")
|
||||
expected_enrollment = _expected_enrollment(
|
||||
current, live, plan, plan_json, plan_sha256
|
||||
)
|
||||
expected_policies = _expected_policy_rows(current, live, plan_sha256)
|
||||
_need(
|
||||
all(session.get(Policy, item.id) is None for item in expected_policies),
|
||||
"ownership_resource_conflict",
|
||||
)
|
||||
|
||||
now = utc_now()
|
||||
expected_enrollment.created_at = now
|
||||
expected_enrollment.updated_at = now
|
||||
expected_enrollment.last_verified_at = now
|
||||
expected_enrollment.completed_at = now
|
||||
for policy in expected_policies:
|
||||
session.add(policy)
|
||||
session.add(expected_enrollment)
|
||||
session.flush()
|
||||
|
||||
updated = _advance_or_verify_ownership(
|
||||
current, _ownership_evidence(live, plan_sha256)
|
||||
)
|
||||
next_revision = _write_journal_update(
|
||||
session, row, current, updated, expected_revision
|
||||
)
|
||||
try:
|
||||
session.commit()
|
||||
except (SQLAlchemyError, RuntimeError, OSError):
|
||||
replay = _readback_owned(session, live)
|
||||
if replay is not None:
|
||||
return replay
|
||||
_record_uncertain_commit(session, expected_revision, live["receiptSha256"])
|
||||
raise RepoPulseOwnershipError("appops_commit_uncertain") from None
|
||||
return journal._status(updated, next_revision)
|
||||
except RepoPulseOwnershipError:
|
||||
session.rollback()
|
||||
raise
|
||||
except journal.RepoPulseAdoptionJournalError as error:
|
||||
session.rollback()
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
except protocol.AdoptionProtocolError as error:
|
||||
session.rollback()
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
except SQLAlchemyError:
|
||||
session.rollback()
|
||||
raise RepoPulseOwnershipError("ownership_storage_unavailable") from None
|
||||
|
||||
|
||||
def complete_repopulse_adoption(
|
||||
session: Session,
|
||||
authenticated_controller_registration: object,
|
||||
*,
|
||||
expected_revision: int,
|
||||
) -> dict[str, Any]:
|
||||
"""Persist AppOps completion after ownership, with fresh receipt readback.
|
||||
|
||||
Controller authentication and its completed adoption/deployment receipt
|
||||
verification remain caller responsibilities. This step independently
|
||||
rechecks the receipt binding and exact owned rows on every replay.
|
||||
"""
|
||||
try:
|
||||
row, current = journal._load(session)
|
||||
_need(row is not None and current is not None, "adoption_intent_not_reserved")
|
||||
live = _verify_live_registration(current, authenticated_controller_registration)
|
||||
plan, plan_json, plan_sha256 = _fixed_plan(session, current)
|
||||
_verify_owned_rows(session, current, live, plan, plan_json, plan_sha256)
|
||||
_advance_or_verify_ownership(current, _ownership_evidence(live, plan_sha256))
|
||||
_need(
|
||||
(current["phase"] == "appops_owned" and current["status"] == "in_progress")
|
||||
or (current["phase"] == "completed" and current["status"] == "completed"),
|
||||
"adoption_not_ready_to_complete",
|
||||
)
|
||||
completion_evidence = {
|
||||
"intentId": current["intentId"],
|
||||
"repository": protocol.REPOSITORY,
|
||||
"applicationId": protocol.APPLICATION_ID,
|
||||
"enrollmentId": live["enrollmentId"],
|
||||
"controllerReceiptSha256": live["receiptSha256"],
|
||||
"enrollmentStillPersisted": True,
|
||||
"exactApplyPolicyStillPersisted": True,
|
||||
"exactRollbackPolicyStillPersisted": True,
|
||||
"controllerReceiptStillMatches": True,
|
||||
"legacyVolumeRetained": live["legacyVolumeRetained"],
|
||||
}
|
||||
if current["phase"] == "completed":
|
||||
_assert_completion_binding(current, live, live)
|
||||
_need(
|
||||
current["evidence"].get("completion") == completion_evidence,
|
||||
"completion_binding_mismatch",
|
||||
)
|
||||
return journal.get_repopulse_adoption_status(session)
|
||||
|
||||
_need(
|
||||
isinstance(expected_revision, int)
|
||||
and not isinstance(expected_revision, bool)
|
||||
and expected_revision == row.revision,
|
||||
"journal_revision_conflict",
|
||||
)
|
||||
try:
|
||||
updated = protocol.complete_intent(current, completion_evidence)
|
||||
except protocol.AdoptionProtocolError as error:
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
return journal._persist_updated_intent(
|
||||
session, row, current, updated, expected_revision
|
||||
)
|
||||
except RepoPulseOwnershipError:
|
||||
session.rollback()
|
||||
raise
|
||||
except journal.RepoPulseAdoptionJournalError as error:
|
||||
session.rollback()
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
except protocol.AdoptionProtocolError as error:
|
||||
session.rollback()
|
||||
raise RepoPulseOwnershipError(error.code) from None
|
||||
except SQLAlchemyError:
|
||||
session.rollback()
|
||||
raise RepoPulseOwnershipError("ownership_storage_unavailable") from None
|
||||
Reference in new issue
Block a user