Publish curated Unraid-AppOps-Gateway source from c61e0e1e292914893b40354f904f23dd9fd4a958
This commit is contained in:
1 parent
b47ce43436
commit
9b94d8d0f5
18 files changed
+1651
-75
No files matched your search
@@ -55,6 +55,8 @@ APPROVAL_EXECUTOR_ACTIONS = {
|
||||
"runner.fleet.upgrade_v2",
|
||||
"runner.fleet.specialize",
|
||||
"runner.statistics.configure",
|
||||
"runner.acceptance.preflight",
|
||||
"runner.acceptance.quiesce",
|
||||
"hub.migration.prepare_permissions",
|
||||
"deployment.targets.sync",
|
||||
"deployment.runnerless.provision",
|
||||
@@ -125,7 +127,12 @@ _DIRECT_STEPS = {
|
||||
def action_execution_profile(
|
||||
action: str, policy_decision: str, capability: str
|
||||
) -> dict[str, Any]:
|
||||
if policy_decision == "blocked":
|
||||
if action in {"runner.acceptance.preflight", "runner.acceptance.quiesce"} and policy_decision != "blocked":
|
||||
# The fixed MCP typed authority is separate from Command Center's
|
||||
# manual route; do not advertise an unsupported manual executor.
|
||||
mode = "governance_only"
|
||||
surface = None
|
||||
elif policy_decision == "blocked":
|
||||
mode = "blocked"
|
||||
surface = None
|
||||
elif action in GENERIC_EXECUTOR_ACTIONS:
|
||||
@@ -1669,6 +1676,31 @@ def _execute_controller_artifact_promote(
|
||||
return operation_result
|
||||
|
||||
|
||||
def _execute_runner_acceptance(session: Session, *, action: str, target: str, source: str,
|
||||
risk: str, reason: str, original_policy_decision: str) -> dict[str, Any]:
|
||||
from ..db.models import GatewayUser
|
||||
from .user_access import actor_identity
|
||||
|
||||
actor = actor_identity.get()
|
||||
admin = False
|
||||
if actor:
|
||||
provider, _, subject = actor.partition(":")
|
||||
user = session.exec(select(GatewayUser).where(GatewayUser.provider == provider,
|
||||
GatewayUser.subject == subject)).first()
|
||||
admin = bool(user and user.enabled and user.role == "admin")
|
||||
authorized = (source == "chatgpt-mcp" and actor is None) or admin
|
||||
if (target != "tower/ci-acceptance-residue" or not authorized
|
||||
or source not in {"manual", "chatgpt-mcp", "approved_operator"}
|
||||
or original_policy_decision not in {"allowed", "approval_required"}):
|
||||
return {"success": False, "status": "blocked", "result": "blocked",
|
||||
"message": "Fixed CI maintenance requires the authenticated administrator boundary.",
|
||||
"policyDecision": "blocked"}
|
||||
return _execute_runner_fleet_upgrade_v2(
|
||||
session, source=source, risk=risk, reason=reason,
|
||||
original_policy_decision=original_policy_decision, acceptance=action.rsplit(".", 1)[-1],
|
||||
)
|
||||
|
||||
|
||||
def _execute_runner_fleet_upgrade_v2(
|
||||
session: Session,
|
||||
*,
|
||||
@@ -1678,12 +1710,15 @@ def _execute_runner_fleet_upgrade_v2(
|
||||
original_policy_decision: str,
|
||||
specialized: bool = False,
|
||||
statistics_configuration: bool = False,
|
||||
acceptance: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
from .deployment_broker import DeploymentBrokerClient
|
||||
|
||||
action = ("runner.statistics.configure" if statistics_configuration else
|
||||
action = ("runner.acceptance." + acceptance if acceptance else
|
||||
"runner.statistics.configure" if statistics_configuration else
|
||||
"runner.fleet.specialize" if specialized else "runner.fleet.upgrade_v2")
|
||||
target = ("tower/projectbrain-runner-statistics" if statistics_configuration else
|
||||
target = ("tower/ci-acceptance-residue" if acceptance else
|
||||
"tower/projectbrain-runner-statistics" if statistics_configuration else
|
||||
"tower/runner-fleet-specialized" if specialized else "tower/runner-fleet-v2")
|
||||
steps = [
|
||||
"Validate exact fixed runner-fleet target",
|
||||
@@ -1697,6 +1732,9 @@ def _execute_runner_fleet_upgrade_v2(
|
||||
steps = ["Verify fixed read-only statistics target", "Prove protected controller/runtime source",
|
||||
"Lock production configuration", "Provision private independent credential with durable CAS",
|
||||
"Configure ProjectBrain for its governed delivery"]
|
||||
if acceptance:
|
||||
steps = ["Prove protected controller/runtime source", "Attest finite CI origin and complete private identities",
|
||||
"Verify dispatch barrier and production exclusion", "Preserve all resources and data"]
|
||||
job = create_job(
|
||||
session,
|
||||
action_id=action,
|
||||
@@ -1710,26 +1748,57 @@ def _execute_runner_fleet_upgrade_v2(
|
||||
set_job_progress(session, job, 25, f"Requesting {action} through the fixed controller endpoint.")
|
||||
try:
|
||||
broker = DeploymentBrokerClient()
|
||||
result = (broker.runner_statistics_configure(reason=reason) if statistics_configuration else
|
||||
result = (broker.runner_acceptance(reason=reason, preflight=acceptance == "preflight") if acceptance else
|
||||
broker.runner_statistics_configure(reason=reason) if statistics_configuration else
|
||||
broker.runner_fleet_specialize(reason=reason) if specialized else broker.runner_fleet_upgrade_v2(reason=reason))
|
||||
if acceptance:
|
||||
allowed_blockers = {
|
||||
"terminal_origin_claim_missing", "terminal_origin_task_binding_unavailable",
|
||||
"terminal_origin_unavailable", "active_product_receipt_exclusion_unavailable",
|
||||
"dispatch_barrier_guard_unverified", "preflight_evidence_rejected",
|
||||
"acceptance_evidence_rejected", "term_timeout", "controller_response_untrusted",
|
||||
"maintenance_outcome_unknown", "maintenance_receipt_untrusted", "maintenance_execution_deadline",
|
||||
}
|
||||
blockers = result.get("blockers", [])
|
||||
clean_blockers = (blockers if isinstance(blockers, list) and len(blockers) <= 20
|
||||
and all(isinstance(code, str) and code in allowed_blockers for code in blockers)
|
||||
else ["controller_response_untrusted"])
|
||||
result = {
|
||||
"status": result.get("status") if result.get("status") in {
|
||||
"ready", "blocked", "partial", "quiesced", "already_quiesced", "unknown", "in_progress"
|
||||
} else "blocked",
|
||||
"revision": result.get("revision") if isinstance(result.get("revision"), str)
|
||||
and len(result["revision"]) == 40 and all(char in "0123456789abcdef" for char in result["revision"]) else None,
|
||||
"blockers": clean_blockers,
|
||||
"preservedContainers": 18 if result.get("preservedContainers") == 18 else None,
|
||||
"effectsPerformed": result.get("effectsPerformed") if type(result.get("effectsPerformed")) is bool else None,
|
||||
"noMutation": result.get("noMutation") if type(result.get("noMutation")) is bool else None,
|
||||
"maintenancePhase": result.get("maintenance", {}).get("phase") if isinstance(result.get("maintenance"), dict)
|
||||
and result["maintenance"].get("phase") in {"guarding", "quiescing", "restoring", "complete"} else result.get("phase")
|
||||
if result.get("phase") in {"guarding", "quiescing", "restoring", "complete"} else None,
|
||||
}
|
||||
status = str(result.get("status") or "error")
|
||||
success = status in ({"configured", "already_configured"} if statistics_configuration else
|
||||
success = status in ({"ready"} if acceptance == "preflight" else {"quiesced", "already_quiesced"} if acceptance else
|
||||
{"configured", "already_configured"} if statistics_configuration else
|
||||
{"specialized", "already_applied"} if specialized else {"upgraded", "already_applied"})
|
||||
message = str(
|
||||
result.get("reason")
|
||||
or (
|
||||
("ProjectBrain read-only statistics configuration verified." if statistics_configuration else
|
||||
("Finite CI acceptance maintenance verified." if acceptance else
|
||||
"ProjectBrain read-only statistics configuration verified." if statistics_configuration else
|
||||
"Tower runner specialization verified." if specialized
|
||||
else "Tower runner-fleet v2 migration verified.")
|
||||
if success else
|
||||
("ProjectBrain statistics configuration was rejected or failed." if statistics_configuration else
|
||||
("Maintenance outcome is unknown or still active; use read-only preflight status before any explicit resume." if acceptance and status in {"unknown", "in_progress", "partial"} else
|
||||
"Finite CI acceptance maintenance is blocked by required evidence." if acceptance else
|
||||
"ProjectBrain statistics configuration was rejected or failed." if statistics_configuration else
|
||||
"Tower runner specialization was rejected or failed." if specialized
|
||||
else "Tower runner-fleet v2 migration was rejected or failed.")
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
result = {"status": "error"}
|
||||
status = "error"
|
||||
result = {"status": "unknown" if acceptance == "quiesce" else "error", "effectsPerformed": None}
|
||||
status = result["status"]
|
||||
success = False
|
||||
message = f"{type(exc).__name__}: fixed controller operation failed"
|
||||
|
||||
@@ -1749,7 +1818,7 @@ def _execute_runner_fleet_upgrade_v2(
|
||||
risk=risk,
|
||||
policy_decision="allowed",
|
||||
reason=reason,
|
||||
result="success" if success else "failed",
|
||||
result="success" if success else "unknown" if acceptance and status in {"unknown", "in_progress", "partial"} else "failed",
|
||||
execution_plan={"steps": steps, "jobId": completed.id},
|
||||
after_state={
|
||||
"status": status,
|
||||
@@ -1768,7 +1837,7 @@ def _execute_runner_fleet_upgrade_v2(
|
||||
return {
|
||||
"success": success,
|
||||
"status": "success" if success else status,
|
||||
"result": "success" if success else "failed",
|
||||
"result": "success" if success else "unknown" if acceptance and status in {"unknown", "in_progress", "partial"} else "failed",
|
||||
"message": message,
|
||||
"jobId": completed.id,
|
||||
"auditId": audit.id,
|
||||
@@ -1778,6 +1847,11 @@ def _execute_runner_fleet_upgrade_v2(
|
||||
"revision": result.get("revision"),
|
||||
"verifiedRunners": result.get("verifiedRunners"),
|
||||
"projectbrainDeliveryRequired": result.get("projectbrainDeliveryRequired"),
|
||||
"blockers": result.get("blockers") if acceptance else None,
|
||||
"preservedContainers": result.get("preservedContainers") if acceptance else None,
|
||||
"effectsPerformed": result.get("effectsPerformed") if acceptance else None,
|
||||
"noMutation": result.get("noMutation") if acceptance else None,
|
||||
"maintenancePhase": result.get("maintenancePhase") if acceptance else None,
|
||||
}
|
||||
|
||||
|
||||
@@ -2065,6 +2139,10 @@ def execute_governed_action(
|
||||
"originalPolicyDecision": original_policy_decision,
|
||||
}
|
||||
|
||||
if action in {"runner.acceptance.preflight", "runner.acceptance.quiesce"}:
|
||||
return _execute_runner_acceptance(session, action=action, target=target, source=source,
|
||||
risk=risk, reason=reason, original_policy_decision=original_policy_decision)
|
||||
|
||||
if action == "controller.artifact.promote":
|
||||
if target.strip().lower() != "tower/deployment-controller":
|
||||
return {
|
||||
@@ -2603,6 +2681,13 @@ def execute_approved_action(session: Session, approval: Approval) -> dict[str, A
|
||||
original_policy_decision="approval_required",
|
||||
)
|
||||
|
||||
if approval.action in {"runner.acceptance.preflight", "runner.acceptance.quiesce"}:
|
||||
return _execute_runner_acceptance(
|
||||
session, action=approval.action, target=approval.target, source="approved_operator",
|
||||
risk=approval.risk, reason=approval.reason or f"Approved through {approval.id}.",
|
||||
original_policy_decision="approval_required",
|
||||
)
|
||||
|
||||
if approval.action == "runner.fleet.upgrade_v2":
|
||||
if approval.target.strip().lower() != "tower/runner-fleet-v2":
|
||||
return {
|
||||
|
||||
@@ -9,6 +9,8 @@ ACTIONS = [
|
||||
'runner.fleet.upgrade_v2',
|
||||
'runner.fleet.specialize',
|
||||
'runner.statistics.configure',
|
||||
'runner.acceptance.preflight',
|
||||
'runner.acceptance.quiesce',
|
||||
'controller.artifact.promote',
|
||||
'controller.managed-storage.initialize',
|
||||
'hub.migration.prepare_permissions',
|
||||
@@ -222,6 +224,8 @@ ACTION_RISK = {
|
||||
'runner.fleet.upgrade_v2': 'high',
|
||||
'runner.fleet.specialize': 'high',
|
||||
'runner.statistics.configure': 'high',
|
||||
'runner.acceptance.preflight': 'low',
|
||||
'runner.acceptance.quiesce': 'high',
|
||||
'controller.artifact.promote': 'high',
|
||||
'controller.managed-storage.initialize': 'high',
|
||||
'hub.migration.prepare_permissions': 'high',
|
||||
@@ -553,6 +557,8 @@ def action_default_target(action: str) -> str:
|
||||
return 'tower/runner-fleet-v2'
|
||||
if action == 'runner.fleet.specialize':
|
||||
return 'tower/runner-fleet-specialized'
|
||||
if action in {'runner.acceptance.preflight', 'runner.acceptance.quiesce'}:
|
||||
return 'tower/ci-acceptance-residue'
|
||||
if action == 'runner.statistics.configure':
|
||||
return 'tower/projectbrain-runner-statistics'
|
||||
if action == 'deployment.targets.sync':
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any
|
||||
|
||||
SOURCE_REPOSITORY = "jens/unraid-appops-gateway"
|
||||
HELPER_NAME = "itworx-controller-artifact-promoter-modern"
|
||||
CONTROLLER_VERSION = "2.1.20"
|
||||
CONTROLLER_VERSION = "2.1.21"
|
||||
HELPER_LABEL = "itworx.controller.promoter"
|
||||
_REVISION = re.compile(r"^[0-9a-f]{40}$")
|
||||
_IMAGE_ID = re.compile(r"^sha256:[0-9a-f]{64}$")
|
||||
|
||||
@@ -174,6 +174,7 @@ class DeploymentBrokerClient:
|
||||
secret: str,
|
||||
payload: dict[str, Any] | None = None,
|
||||
error_response_fields: frozenset[str] = frozenset(),
|
||||
request_timeout: httpx.Timeout | None = None,
|
||||
) -> dict[str, Any]:
|
||||
if not self.is_configured():
|
||||
return {"status": "missing_config", "reason": self.configuration_error()}
|
||||
@@ -194,7 +195,7 @@ class DeploymentBrokerClient:
|
||||
headers["Content-Type"] = "application/json"
|
||||
try:
|
||||
with httpx.Client(
|
||||
timeout=self.timeout_seconds,
|
||||
timeout=request_timeout or self.timeout_seconds,
|
||||
verify=self._verify_option(),
|
||||
) as client:
|
||||
response = client.request(method, f"{self.base_url}{path}", content=body, headers=headers)
|
||||
@@ -990,6 +991,17 @@ class DeploymentBrokerClient:
|
||||
secret=settings.mcp_token or "", payload={"reason": reason[:1000]},
|
||||
)
|
||||
|
||||
def runner_acceptance(self, *, reason: str, preflight: bool) -> dict[str, Any]:
|
||||
result = self._request(
|
||||
"POST", "/v1/appops/runner-acceptance/preflight" if preflight else "/v1/appops/runner-acceptance/quiesce",
|
||||
secret=settings.mcp_token or "", payload={"reason": reason[:1000]},
|
||||
request_timeout=httpx.Timeout(connect=5, write=5, pool=5, read=350),
|
||||
)
|
||||
if not preflight and result.get("status") in {"unreachable", "error", "rejected"}:
|
||||
return {"status": "unknown", "noMutation": False, "effectsPerformed": None,
|
||||
"blockers": ["maintenance_outcome_unknown"]}
|
||||
return result
|
||||
|
||||
def hub_migration_prepare_permissions(self, *, reason: str) -> dict[str, Any]:
|
||||
"""Prepare only the fixed Hub backup and controller-state permissions."""
|
||||
return self._request(
|
||||
|
||||
@@ -59,6 +59,14 @@ def _reconciliation_required_response(
|
||||
}
|
||||
|
||||
|
||||
def mcp_policy_source(action: str, target: str) -> str:
|
||||
"""Normalize only the fixed acceptance authority at the authenticated MCP boundary."""
|
||||
if (action in {"runner.acceptance.preflight", "runner.acceptance.quiesce"}
|
||||
and target == "tower/ci-acceptance-residue"):
|
||||
return "chatgpt-mcp"
|
||||
return "automation"
|
||||
|
||||
|
||||
def request_operation(
|
||||
session: Session,
|
||||
*,
|
||||
@@ -97,7 +105,7 @@ def request_operation(
|
||||
session,
|
||||
action=safe_action,
|
||||
target=safe_target,
|
||||
source="automation",
|
||||
source=mcp_policy_source(safe_action, safe_target),
|
||||
)
|
||||
risk = str(policy.get("risk") or ACTION_RISK.get(safe_action, "critical"))
|
||||
if capability == "read_only":
|
||||
|
||||
@@ -210,6 +210,16 @@ def evaluate_policy(
|
||||
"scope_decision": "allowed" if allowed else None,
|
||||
}
|
||||
|
||||
if action in {'runner.acceptance.preflight', 'runner.acceptance.quiesce'}:
|
||||
# Dedicated finite maintenance authority; wildcard and database rows
|
||||
# cannot redirect this source-qualified action to other containers.
|
||||
eligible = target == 'tower/ci-acceptance-residue' and source in {'manual', 'chatgpt-mcp'}
|
||||
return {
|
||||
'decision': ('allowed' if action.endswith('.preflight') else 'approval_required') if eligible else 'blocked',
|
||||
'reason': 'Fixed CI acceptance maintenance requires current native origin, dispatch and product-exclusion proofs.',
|
||||
'risk': ACTION_RISK[action], 'policy_id': None, 'scope_decision': None,
|
||||
}
|
||||
|
||||
if action in _DEFAULT_DECISIONS:
|
||||
reason = _DEFAULT_REASONS.get(action, 'Action uses default governance policy.')
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user