fix(reboot): gate windows99 vmx candidate confirmation
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m25s
CD Pipeline / build-and-deploy (push) Successful in 5m23s
CD Pipeline / post-deploy-checks (push) Successful in 1m47s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m25s
CD Pipeline / build-and-deploy (push) Successful in 5m23s
CD Pipeline / post-deploy-checks (push) Successful in 1m47s
This commit is contained in:
@@ -3509,6 +3509,45 @@ def _enrich_from_current_readbacks(
|
||||
state["windows99_vmx_source_locator_relink_dry_run_plan_id"] = str(
|
||||
locator_relink_plan.get("plan_id") or ""
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_confirmation_status"] = str(
|
||||
windows99_vmx_source_locator_readback.get("candidate_confirmation_status")
|
||||
or "unknown"
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_identity_decision"] = str(
|
||||
windows99_vmx_source_locator_readback.get("candidate_identity_decision")
|
||||
or "unknown"
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_identity_evidence_sufficient"] = bool(
|
||||
windows99_vmx_source_locator_readback.get(
|
||||
"candidate_identity_evidence_sufficient"
|
||||
)
|
||||
is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_relink_dry_run_ready"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("candidate_relink_dry_run_ready")
|
||||
is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_confirmation_missing_evidence"] = (
|
||||
_strings(
|
||||
windows99_vmx_source_locator_readback.get(
|
||||
"candidate_confirmation_missing_evidence"
|
||||
)
|
||||
)
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_confirmation_safe_next_step"] = str(
|
||||
windows99_vmx_source_locator_readback.get(
|
||||
"candidate_confirmation_safe_next_step"
|
||||
)
|
||||
or ""
|
||||
)
|
||||
state[
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate"
|
||||
] = bool(
|
||||
windows99_vmx_source_locator_readback.get(
|
||||
"apply_allowed_by_candidate_confirmation_gate"
|
||||
)
|
||||
is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_source_count_known"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("candidate_source_count_known")
|
||||
is True
|
||||
@@ -3948,6 +3987,35 @@ def _enrich_from_current_readbacks(
|
||||
evidence["windows99_vmx_source_locator_relink_dry_run_plan_id"] = state[
|
||||
"windows99_vmx_source_locator_relink_dry_run_plan_id"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_candidate_confirmation_status"] = state[
|
||||
"windows99_vmx_source_locator_candidate_confirmation_status"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_candidate_identity_decision"] = state[
|
||||
"windows99_vmx_source_locator_candidate_identity_decision"
|
||||
]
|
||||
evidence[
|
||||
"windows99_vmx_source_locator_candidate_identity_evidence_sufficient"
|
||||
] = state[
|
||||
"windows99_vmx_source_locator_candidate_identity_evidence_sufficient"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_candidate_relink_dry_run_ready"] = state[
|
||||
"windows99_vmx_source_locator_candidate_relink_dry_run_ready"
|
||||
]
|
||||
evidence[
|
||||
"windows99_vmx_source_locator_candidate_confirmation_missing_evidence"
|
||||
] = state[
|
||||
"windows99_vmx_source_locator_candidate_confirmation_missing_evidence"
|
||||
]
|
||||
evidence[
|
||||
"windows99_vmx_source_locator_candidate_confirmation_safe_next_step"
|
||||
] = state[
|
||||
"windows99_vmx_source_locator_candidate_confirmation_safe_next_step"
|
||||
]
|
||||
evidence[
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate"
|
||||
] = state[
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_candidate_source_count_known"] = state[
|
||||
"windows99_vmx_source_locator_candidate_source_count_known"
|
||||
]
|
||||
@@ -4765,6 +4833,43 @@ def _set_rollups_and_summary(
|
||||
"windows99_vmx_source_locator_relink_dry_run_plan_id": str(
|
||||
state.get("windows99_vmx_source_locator_relink_dry_run_plan_id") or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_confirmation_status": str(
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_confirmation_status"
|
||||
)
|
||||
or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_identity_decision": str(
|
||||
state.get("windows99_vmx_source_locator_candidate_identity_decision")
|
||||
or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_identity_evidence_sufficient": (
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_identity_evidence_sufficient"
|
||||
)
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_relink_dry_run_ready": (
|
||||
state.get("windows99_vmx_source_locator_candidate_relink_dry_run_ready")
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_confirmation_missing_evidence": _strings(
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_confirmation_missing_evidence"
|
||||
)
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_confirmation_safe_next_step": str(
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_confirmation_safe_next_step"
|
||||
)
|
||||
or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate": (
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate"
|
||||
)
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_source_count_known": (
|
||||
state.get("windows99_vmx_source_locator_candidate_source_count_known")
|
||||
is True
|
||||
@@ -5112,6 +5217,43 @@ def _set_rollups_and_summary(
|
||||
"windows99_vmx_source_locator_relink_dry_run_plan_id": str(
|
||||
state.get("windows99_vmx_source_locator_relink_dry_run_plan_id") or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_confirmation_status": str(
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_confirmation_status"
|
||||
)
|
||||
or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_identity_decision": str(
|
||||
state.get("windows99_vmx_source_locator_candidate_identity_decision")
|
||||
or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_identity_evidence_sufficient": (
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_identity_evidence_sufficient"
|
||||
)
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_relink_dry_run_ready": (
|
||||
state.get("windows99_vmx_source_locator_candidate_relink_dry_run_ready")
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_confirmation_missing_evidence": _strings(
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_confirmation_missing_evidence"
|
||||
)
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_confirmation_safe_next_step": str(
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_candidate_confirmation_safe_next_step"
|
||||
)
|
||||
or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate": (
|
||||
state.get(
|
||||
"windows99_vmx_source_locator_apply_allowed_by_candidate_confirmation_gate"
|
||||
)
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_source_count_known": (
|
||||
state.get("windows99_vmx_source_locator_candidate_source_count_known")
|
||||
is True
|
||||
|
||||
@@ -26,6 +26,9 @@ _SCHEMA_VERSION = "windows99_vmx_source_locator_readback_v1"
|
||||
_COLLECTOR_SCHEMA_VERSION = "windows99_vmx_source_locator_collector_v1"
|
||||
_LOCATOR_SCHEMA_VERSION = "windows99_vmx_source_locator_v1"
|
||||
_RECEIPT_SCHEMA_VERSION = "windows99_vmx_source_locator_receipt_v1"
|
||||
_CANDIDATE_CONFIRMATION_SCHEMA_VERSION = (
|
||||
"windows99_vmx_source_candidate_confirmation_gate_v1"
|
||||
)
|
||||
_RECEIPT_FILE = "windows99-vmx-source-locator-receipt.snapshot.json"
|
||||
_COLLECTOR_SCRIPT_PATH = "scripts/reboot-recovery/locate-windows99-vmx-source.sh"
|
||||
_LOCATOR_SCRIPT_PATH = "scripts/reboot-recovery/windows99-vmx-source-locator.ps1"
|
||||
@@ -89,6 +92,120 @@ def _vm_rows_for_aliases(
|
||||
return rows
|
||||
|
||||
|
||||
def _build_candidate_confirmation_gate(
|
||||
*,
|
||||
receipt_applies: bool,
|
||||
receipt: dict[str, Any],
|
||||
target_aliases: list[str],
|
||||
receipt_locate_status: str,
|
||||
) -> dict[str, Any]:
|
||||
alias_hint_count = _int(receipt.get("alias_hint_candidate_count"), 0) or 0
|
||||
identity_target_hint_count = (
|
||||
_int(receipt.get("identity_target_hint_count"), 0) or 0
|
||||
)
|
||||
identity_unassigned_count = (
|
||||
_int(receipt.get("identity_unassigned_ubuntu_candidate_count"), 0) or 0
|
||||
)
|
||||
candidate_source_count = _int(receipt.get("candidate_source_count"), 0) or 0
|
||||
candidate_confirmation_required = bool(
|
||||
receipt_applies and "requires_confirmation" in receipt_locate_status
|
||||
)
|
||||
confirmed_candidate_count = 0
|
||||
identity_evidence_sufficient = False
|
||||
status = "not_required_no_candidate_confirmation"
|
||||
decision = "not_required"
|
||||
reason = "no active candidate confirmation gate"
|
||||
safe_next_step = "run_locator_collect_or_keep_no_source_locator_required"
|
||||
missing_evidence: list[str] = []
|
||||
|
||||
if not receipt_applies:
|
||||
status = "blocked_locator_receipt_missing"
|
||||
decision = "blocked"
|
||||
reason = "locator receipt is not attached to the current target aliases"
|
||||
safe_next_step = (
|
||||
"run_no_secret_windows99_vmx_source_locator_collect_then_rerun_"
|
||||
"candidate_confirmation_gate"
|
||||
)
|
||||
missing_evidence = ["collector_receipt"]
|
||||
elif alias_hint_count == 1 or identity_target_hint_count == 1:
|
||||
confirmed_candidate_count = 1
|
||||
identity_evidence_sufficient = True
|
||||
status = "candidate_identity_confirmed_ready_for_scoped_relink_dry_run"
|
||||
decision = "confirmed_for_dry_run"
|
||||
reason = "exactly one candidate carries target alias or target identity evidence"
|
||||
safe_next_step = (
|
||||
"build_scoped_relink_dry_run_with_rollback_and_post_verifier_"
|
||||
"no_vm_power_change"
|
||||
)
|
||||
elif alias_hint_count > 1 or identity_target_hint_count > 1:
|
||||
confirmed_candidate_count = alias_hint_count + identity_target_hint_count
|
||||
status = "blocked_multiple_target_hint_candidates"
|
||||
decision = "blocked"
|
||||
reason = "multiple candidates carry target-like evidence"
|
||||
safe_next_step = "collect_authorized_console_candidate_identity_evidence_then_rerun_gate"
|
||||
missing_evidence = ["single_target_candidate"]
|
||||
elif candidate_confirmation_required:
|
||||
status = "blocked_single_unassigned_ubuntu_candidate_identity_not_confirmed"
|
||||
decision = "blocked"
|
||||
reason = (
|
||||
"candidate is still generic Ubuntu evidence; no target alias, MAC, UUID, "
|
||||
"or display-name proof ties it to 111"
|
||||
)
|
||||
safe_next_step = (
|
||||
"collect_authorized_console_candidate_identity_evidence_or_verified_"
|
||||
"backup_path_then_rerun_confirmation_gate"
|
||||
)
|
||||
missing_evidence = [
|
||||
"target_alias_identity_hint",
|
||||
"verified_backup_or_authorized_console_candidate_identity",
|
||||
"pre_apply_backup_plan",
|
||||
"scoped_relink_dry_run",
|
||||
]
|
||||
elif candidate_source_count <= 0:
|
||||
status = "blocked_no_candidate_source_found"
|
||||
decision = "blocked"
|
||||
reason = "locator found no candidate source for the missing VMX alias"
|
||||
safe_next_step = (
|
||||
"provide_existing_vmx_source_or_verified_backup_path_then_rerun_"
|
||||
"locator_check_mode"
|
||||
)
|
||||
missing_evidence = ["existing_vmx_source_or_verified_backup_path"]
|
||||
|
||||
can_prepare_scoped_relink_dry_run = bool(
|
||||
identity_evidence_sufficient and confirmed_candidate_count == 1
|
||||
)
|
||||
|
||||
return {
|
||||
"schema_version": _CANDIDATE_CONFIRMATION_SCHEMA_VERSION,
|
||||
"status": status,
|
||||
"decision": decision,
|
||||
"reason": reason,
|
||||
"target_aliases": target_aliases,
|
||||
"candidate_confirmation_required": candidate_confirmation_required,
|
||||
"candidate_source_count": candidate_source_count,
|
||||
"alias_hint_candidate_count": alias_hint_count,
|
||||
"identity_target_hint_count": identity_target_hint_count,
|
||||
"identity_unassigned_ubuntu_candidate_count": identity_unassigned_count,
|
||||
"confirmed_candidate_count": confirmed_candidate_count,
|
||||
"identity_evidence_sufficient": identity_evidence_sufficient,
|
||||
"can_prepare_scoped_relink_dry_run": can_prepare_scoped_relink_dry_run,
|
||||
"apply_allowed_by_confirmation_gate": False,
|
||||
"vm_power_change_allowed": False,
|
||||
"remote_write_performed": False,
|
||||
"vm_power_change_performed": False,
|
||||
"secret_value_read": False,
|
||||
"safe_next_step": safe_next_step,
|
||||
"missing_evidence": missing_evidence,
|
||||
"required_evidence": [
|
||||
"single_target_candidate_identity",
|
||||
"source_path_fingerprint_or_authorized_console_artifact",
|
||||
"pre_apply_backup_plan",
|
||||
"scoped_relink_dry_run",
|
||||
"post_apply_no_secret_verifier",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def build_windows99_vmx_source_locator_readback(
|
||||
scorecard: dict[str, Any],
|
||||
*,
|
||||
@@ -282,6 +399,13 @@ def build_windows99_vmx_source_locator_readback(
|
||||
"vm_power_change_performed": False,
|
||||
}
|
||||
|
||||
candidate_confirmation_gate = _build_candidate_confirmation_gate(
|
||||
receipt_applies=receipt_applies,
|
||||
receipt=receipt,
|
||||
target_aliases=target_aliases,
|
||||
receipt_locate_status=receipt_locate_status,
|
||||
)
|
||||
|
||||
return {
|
||||
"schema_version": _SCHEMA_VERSION,
|
||||
"generated_at": generated_at
|
||||
@@ -342,6 +466,30 @@ def build_windows99_vmx_source_locator_readback(
|
||||
"controlled_relink_dry_run_ready": controlled_relink_dry_run_ready,
|
||||
"controlled_relink_dry_run_blocker": controlled_relink_dry_run_blocker,
|
||||
"controlled_relink_dry_run_plan": relink_dry_run_plan,
|
||||
"candidate_confirmation_gate": candidate_confirmation_gate,
|
||||
"candidate_confirmation_status": str(
|
||||
candidate_confirmation_gate.get("status") or "unknown"
|
||||
),
|
||||
"candidate_identity_decision": str(
|
||||
candidate_confirmation_gate.get("decision") or "unknown"
|
||||
),
|
||||
"candidate_identity_evidence_sufficient": bool(
|
||||
candidate_confirmation_gate.get("identity_evidence_sufficient") is True
|
||||
),
|
||||
"candidate_relink_dry_run_ready": bool(
|
||||
candidate_confirmation_gate.get("can_prepare_scoped_relink_dry_run")
|
||||
is True
|
||||
),
|
||||
"candidate_confirmation_missing_evidence": _strings(
|
||||
candidate_confirmation_gate.get("missing_evidence")
|
||||
),
|
||||
"candidate_confirmation_safe_next_step": str(
|
||||
candidate_confirmation_gate.get("safe_next_step") or ""
|
||||
),
|
||||
"apply_allowed_by_candidate_confirmation_gate": bool(
|
||||
candidate_confirmation_gate.get("apply_allowed_by_confirmation_gate")
|
||||
is True
|
||||
),
|
||||
"public_lan_topology_redacted": True,
|
||||
"redacted_public_display_only": True,
|
||||
"raw_path_output": False,
|
||||
|
||||
Reference in New Issue
Block a user