410 lines
23 KiB
Python
410 lines
23 KiB
Python
from __future__ import annotations
|
|
|
|
from govoplan_core.core.modules import with_documentation_structured_translations
|
|
from govoplan_ops.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
|
|
from govoplan_ops.backend.infrastructure import (
|
|
OPS_INFRASTRUCTURE_DEPENDENCY_CAPABILITY,
|
|
OpsInfrastructureDependencyProvider,
|
|
)
|
|
|
|
from govoplan_core.core.access import (
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
)
|
|
from govoplan_core.core.modules import (
|
|
DocumentationCondition,
|
|
DocumentationLink,
|
|
DocumentationTopic,
|
|
FrontendModule,
|
|
FrontendRoute,
|
|
ModuleContext,
|
|
ModuleManifest,
|
|
NavItem,
|
|
PermissionDefinition,
|
|
RoleTemplate,
|
|
)
|
|
from govoplan_core.core.provider_governance import (
|
|
ModuleArchitectureDeclaration,
|
|
ModuleArchitectureDocumentation,
|
|
ModuleMaturityEvidence,
|
|
)
|
|
from govoplan_core.core.operations import RuntimeWorkStatusProviderRegistration
|
|
from govoplan_core.core.runtime_work import celery_runtime_work_status
|
|
from govoplan_core.core.views import ViewSurface
|
|
|
|
OPS_READ_SCOPE = "ops:operations:read"
|
|
OPS_READ_SCOPES = (OPS_READ_SCOPE, "system:settings:read", "admin:settings:read")
|
|
OPS_RUN_SCOPE = "ops:operations:run"
|
|
OPS_RUN_SCOPES = (OPS_RUN_SCOPE, "system:settings:write")
|
|
|
|
ARCHITECTURE = ModuleArchitectureDeclaration(
|
|
layer="runtime_meta",
|
|
kind="operations",
|
|
maturity="vertical_slice",
|
|
evidence=(
|
|
ModuleMaturityEvidence(
|
|
kind="test",
|
|
reference="tests/test_governance_inventory.py",
|
|
summary="Tests safe operational projection of module governance declarations.",
|
|
),
|
|
ModuleMaturityEvidence(
|
|
kind="documentation",
|
|
reference="docs/SCALABILITY_PROFILES.md",
|
|
summary="Documents operational topology and scaling posture.",
|
|
),
|
|
ModuleMaturityEvidence(
|
|
kind="documentation",
|
|
reference="docs/BACKUP_EVIDENCE_STATUS.md",
|
|
summary="Documents the sanitized signed backup and restore status projection.",
|
|
),
|
|
),
|
|
known_limits=(
|
|
"Provider health observations depend on module-owned operational probes and may be unavailable until configured.",
|
|
),
|
|
owned_concepts=("operations status projection", "bounded operational probes"),
|
|
non_owned_concepts=("domain repair", "external provider credentials"),
|
|
documentation=ModuleArchitectureDocumentation(
|
|
recovery=(
|
|
"docs/SCALABILITY_PROFILES.md",
|
|
"docs/BACKUP_EVIDENCE_STATUS.md",
|
|
),
|
|
operations=(
|
|
"docs/SCALABILITY_PROFILES.md",
|
|
"docs/BACKUP_EVIDENCE_STATUS.md",
|
|
),
|
|
),
|
|
)
|
|
|
|
|
|
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
|
|
module_id, resource, action = scope.split(":", 2)
|
|
return PermissionDefinition(
|
|
scope=scope,
|
|
label=label,
|
|
description=description,
|
|
category="Operations",
|
|
level="system",
|
|
module_id=module_id,
|
|
resource=resource,
|
|
action=action,
|
|
)
|
|
|
|
|
|
def _route_factory(context: ModuleContext):
|
|
del context
|
|
from govoplan_ops.backend.api.v1.routes import router
|
|
|
|
return router
|
|
|
|
|
|
manifest = ModuleManifest(
|
|
id="ops",
|
|
name="Ops",
|
|
version="0.1.21",
|
|
required_capabilities=(
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
),
|
|
optional_dependencies=("audit", "docs", "notifications"),
|
|
permissions=(
|
|
_permission(
|
|
OPS_READ_SCOPE,
|
|
"View operations status",
|
|
"Read runtime health, deployment profile, and sizing information.",
|
|
),
|
|
_permission(
|
|
OPS_RUN_SCOPE,
|
|
"Run operational checks",
|
|
"Run bounded module-owned persistence and integration probes.",
|
|
),
|
|
),
|
|
role_templates=(
|
|
RoleTemplate(
|
|
slug="ops_reader",
|
|
name="Operations reader",
|
|
description="Read platform health and deployment profile information.",
|
|
permissions=(OPS_READ_SCOPE,),
|
|
level="system",
|
|
),
|
|
RoleTemplate(
|
|
slug="ops_operator",
|
|
name="Operations operator",
|
|
description="Read platform health and run bounded operational probes.",
|
|
permissions=(OPS_READ_SCOPE, OPS_RUN_SCOPE),
|
|
level="system",
|
|
),
|
|
),
|
|
runtime_work_status_providers=(
|
|
RuntimeWorkStatusProviderRegistration(
|
|
module_id="ops",
|
|
provider_id="core.celery",
|
|
provider=celery_runtime_work_status,
|
|
cache_seconds=15,
|
|
),
|
|
),
|
|
capability_factories={
|
|
OPS_INFRASTRUCTURE_DEPENDENCY_CAPABILITY: (
|
|
lambda context: OpsInfrastructureDependencyProvider()
|
|
),
|
|
},
|
|
documentation=(
|
|
DocumentationTopic(
|
|
id="ops.health-governance-and-sizing",
|
|
title="Inspect platform health and deployment posture",
|
|
summary="Ops combines module-owned health checks with deployment profile, governance inventory, worker assumptions, and sizing guidance.",
|
|
body="Read-only status distinguishes configured capabilities from healthy integrations. Worker and queue providers use a Core runtime-status contract, so Ops never imports a provider backend. The surface distinguishes intentionally disabled, unconfigured, starting, healthy with unsupported queue depth, measured idle, busy, degraded, stale, and unreachable states. It shows enabled/configured state, backend, workers, heartbeat age and stale threshold, queue depth, active/reserved work, and failures only when each value is actually reported; unavailable values are never rendered as zero or healthy. Local development treats intentionally disabled workers as expected, while production profiles require an enabled, configured, reachable provider before queue-backed work is accepted. Polling is bounded to one request, pauses while the page is hidden, and refreshes on return. When the deployment mounts a signed or locally generated non-secret infrastructure capability receipt, Ops shows whether PostgreSQL, Redis, SMTP, file storage, load balancing, and ingress are configured, externally supplied, available but unconfigured, or unavailable. Secret values never cross this boundary; only stable environment or credential-envelope references may be disclosed. Pending post-install tasks remain visible with a stable resume key. The separately authorized infrastructure-dependency endpoint asks enabled module providers for their current persisted configuration and data dependencies. Each provider returns only stable references, states, scopes, numeric metrics, and required migration actions; one failed provider marks the entire fresh inventory incomplete so the host deployer blocks a capability change. Authorized operators can run bounded probes; a probe must not perform unbounded business work or silently repair data. Use readiness and worker results when diagnosing a node, and use the deployment profile and sizing assumptions when planning horizontal capacity.",
|
|
documentation_types=("admin", "user"),
|
|
audience=("operator", "system_admin"),
|
|
related_modules=("audit", "docs", "notifications"),
|
|
links=(
|
|
DocumentationLink(
|
|
label="Infrastructure dependency inventory API",
|
|
href="/api/v1/ops/infrastructure/dependencies",
|
|
kind="api",
|
|
),
|
|
DocumentationLink(
|
|
label="Operations profile handbook",
|
|
href="govoplan-ops/docs/SCALABILITY_PROFILES.md",
|
|
kind="repository",
|
|
),
|
|
),
|
|
translations={
|
|
"de": {
|
|
"title": "Plattformzustand und Bereitstellungsprofil prüfen",
|
|
"summary": "Ops führt modulbezogene Zustandsprüfungen mit Bereitstellungsprofil, Governance-Inventar, Worker-Annahmen und Dimensionierungshinweisen zusammen.",
|
|
"body": (
|
|
"Der schreibgeschützte Status unterscheidet konfigurierte Fähigkeiten von funktionsfähigen Integrationen. "
|
|
"Worker- und Warteschlangenanbieter verwenden einen Core-Vertrag für den Laufzeitstatus, sodass Ops niemals ein Anbieter-Backend importiert. "
|
|
"Die Oberfläche unterscheidet bewusst deaktivierte, nicht konfigurierte, startende, gesunde, untätige, ausgelastete, beeinträchtigte, veraltete und nicht erreichbare Zustände; eine nicht unterstützte Warteschlangentiefe wird ausdrücklich ausgewiesen. "
|
|
"Aktivierung, Konfiguration, Backend, Worker, Alter und Grenzwert des Heartbeats, Warteschlangentiefe, aktive oder reservierte Arbeit sowie Fehler werden nur angezeigt, wenn der Anbieter den jeweiligen Wert tatsächlich meldet; fehlende Werte erscheinen niemals als null oder gesund. "
|
|
"In der lokalen Entwicklung sind bewusst deaktivierte Worker zulässig, während Produktionsprofile einen aktivierten, konfigurierten und erreichbaren Anbieter verlangen, bevor warteschlangengestützte Arbeit angenommen wird. "
|
|
"Die Abfrage bleibt auf eine Anfrage je Intervall begrenzt, pausiert bei ausgeblendeter Seite und wird bei der Rückkehr fortgesetzt. "
|
|
"Ist ein signierter oder lokal erzeugter Infrastrukturbeleg ohne Geheimwerte eingebunden, zeigt Ops für PostgreSQL, Redis, SMTP, Dateispeicher, Lastverteilung und Ingress, ob die Fähigkeit konfiguriert, extern bereitgestellt, verfügbar aber nicht konfiguriert oder nicht verfügbar ist. "
|
|
"Geheimwerte überschreiten diese Grenze nie; offengelegt werden dürfen nur stabile Umgebungs- oder Credential-Envelope-Referenzen. "
|
|
"Ausstehende Aufgaben nach einer Installation bleiben mit einem stabilen Fortsetzungsschlüssel sichtbar. Der separat autorisierte Infrastruktur-Abhängigkeitsendpunkt fragt aktivierte Modulprovider nach ihren aktuellen gespeicherten Konfigurations- und Datenabhängigkeiten. "
|
|
"Jeder Provider liefert nur stabile Referenzen, Zustände, Geltungsbereiche, numerische Kennzahlen und erforderliche Migrationsmaßnahmen; schlägt ein Provider fehl, ist das gesamte frische Inventar unvollständig und der Host-Deployer blockiert die Fähigkeitsänderung. "
|
|
"Autorisierte Betriebsverantwortliche dürfen begrenzte Prüfungen ausführen; eine Prüfung darf weder unbegrenzte Facharbeit auslösen noch Daten stillschweigend reparieren. "
|
|
"Nutzen Sie Bereitschafts- und Worker-Ergebnisse zur Diagnose eines Knotens sowie Bereitstellungsprofil und Dimensionierungsannahmen zur Planung horizontaler Kapazität."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"kind": "reference",
|
|
"help_contexts": [
|
|
"ops.page",
|
|
"ops.page.summary",
|
|
"ops.page.health",
|
|
"ops.page.runtime",
|
|
"ops.page.governance",
|
|
"ops.page.deployment",
|
|
"ops.page.sizing",
|
|
"ops.widget.health",
|
|
"ops.state.read-only",
|
|
],
|
|
"consequence_classes": {
|
|
"run_probes": "run bounded module-owned health probes and refresh the sanitized operational projection",
|
|
},
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="ops.runtime-coordination-and-recovery",
|
|
title="Drain runtime nodes and inspect recovery evidence",
|
|
summary="Ops projects shared runtime heartbeats, replica gaps, drain controls, and recovery states that require operator attention.",
|
|
body="Use the runtime table to identify stale or composition-skewed API and worker replicas. Drain before replacement so API readiness closes and workers stop taking new queue work; cancellation is available while the node is still draining. The recovery table reports durable Core recovery operations. A rejected operation is a verified provider rejection and needs no recovery; outcome-unknown and recovery-required operations still require reconciliation through the owning module. Core module-lifecycle entries block every later install or live graph change: use the installer run id to verify package, backup, migration, and health evidence before rollback or forward repair. Mail SMTP and IMAP APPEND entries use stable attempt identifiers and digest-only evidence: reconcile the Mail command from provider evidence, never by replaying the original effect from Ops. Files blob writes, hard purge, reference-checked garbage collection, and conditional S3 connector writes record Core recovery evidence. For a Files connector outcome, inspect the provider request/content markers and revision before allowing another write to the fenced path; for blob GC, recheck FileVersion references and exact object absence. Development SQLite can show only handled-rollback reconstruction for caller-transaction blob uploads; after a hard SQLite process loss, run the owning Files integrity scan because an orphan may have no Ops ledger row. Dataflow database-only runs are atomic, while published-output runs use forward recovery: reconcile the recorded output digest and sink idempotency key before allowing another publication. Backup status separately projects only the sanitized deployment verification receipt: a verified status identifies a coordinated recovery point and isolated restore drill, while absent, expired, or invalid evidence blocks a release-changing migration.",
|
|
documentation_types=("admin", "user"),
|
|
audience=("operator", "system_admin"),
|
|
conditions=(
|
|
DocumentationCondition(
|
|
required_modules=("ops",),
|
|
any_scopes=OPS_READ_SCOPES,
|
|
),
|
|
),
|
|
related_modules=("audit", "files", "notifications"),
|
|
translations={
|
|
"de": {
|
|
"title": "Laufzeitknoten leeren und Wiederherstellungsnachweise prüfen",
|
|
"summary": "Ops projiziert gemeinsame Laufzeit-Heartbeats, Replikatlücken, Leerungssteuerung und Wiederherstellungszustände, die betriebliche Aufmerksamkeit erfordern.",
|
|
"body": (
|
|
"Verwenden Sie die Laufzeittabelle, um veraltete API- und Worker-Replikate oder Replikate mit abweichender Modulzusammensetzung zu erkennen. "
|
|
"Leeren Sie einen Knoten vor dem Austausch, damit seine API-Bereitschaft geschlossen wird und Worker keine neue Warteschlangenarbeit annehmen; solange der Knoten noch geleert wird, kann der Vorgang abgebrochen werden. "
|
|
"Die Wiederherstellungstabelle zeigt dauerhafte Core-Wiederherstellungsvorgänge. Eine abgelehnte Operation ist eine bestätigte Ablehnung des Anbieters und benötigt keine Wiederherstellung; Vorgänge mit unbekanntem Ergebnis oder erforderlicher Wiederherstellung müssen weiterhin im besitzenden Modul abgeglichen werden. "
|
|
"Core-Einträge zum Modullebenszyklus sperren jede spätere Installation oder Änderung des laufenden Modulgraphen: Prüfen Sie anhand der Installer-Lauf-ID Paket-, Sicherungs-, Migrations- und Zustandsnachweise, bevor Sie zurückrollen oder vorwärts reparieren. "
|
|
"SMTP- und IMAP-APPEND-Einträge von Mail verwenden stabile Versuchskennungen und ausschließlich Digest-Nachweise; gleichen Sie den Mail-Befehl mit Anbieternachweisen ab und wiederholen Sie niemals die ursprüngliche Wirkung aus Ops. "
|
|
"Files erfasst Core-Wiederherstellungsnachweise für Blob-Schreibvorgänge, genehmigte harte Löschung, referenzgeprüfte Speicherbereinigung und bedingte S3-Connector-Schreibvorgänge. Prüfen Sie bei einem Files-Connector-Ergebnis Anbieteranfrage, Inhaltsmerkmale und Revision, bevor ein weiterer Schreibvorgang auf den gesperrten Pfad zugelassen wird; prüfen Sie bei der Blob-Bereinigung erneut FileVersion-Referenzen und die genaue Abwesenheit des Objekts. "
|
|
"SQLite für die Entwicklung kann bei Blob-Uploads innerhalb einer aufrufenden Transaktion nur die Rekonstruktion behandelter Rollbacks zeigen; nach einem harten Prozessverlust muss die Files-Integritätsprüfung ausgeführt werden, weil ein verwaistes Objekt ohne Ops-Ledger-Eintrag existieren kann. "
|
|
"Reine Datenbankläufe von Dataflow sind atomar, während Läufe mit veröffentlichten Ausgaben vorwärts repariert werden: Gleichen Sie den erfassten Ausgabedigest und den Idempotenzschlüssel des Ziels ab, bevor eine weitere Veröffentlichung erlaubt wird. "
|
|
"Der Sicherungsstatus projiziert getrennt nur den bereinigten Bereitstellungsbeleg: Ein verifizierter Status weist einen koordinierten Wiederherstellungspunkt und eine isolierte Wiederherstellungsprobe nach; fehlende, abgelaufene oder ungültige Nachweise sperren eine migrationsbedingte Release-Änderung."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"kind": "workflow",
|
|
"route": "/ops",
|
|
"screen": "Runtime cluster and recovery evidence",
|
|
"steps": [
|
|
"Compare active non-stale nodes with the configured API and worker replica expectations.",
|
|
"Request drain and wait for the node to report draining before replacing it.",
|
|
"Inspect every recovery-required, outcome-unknown, or manual-intervention operation and follow its recorded recovery mode.",
|
|
"For Files, distinguish blob upload/repair, approved hard purge, blob garbage collection, and S3 connector write-back before checking the owning database/object or provider evidence.",
|
|
"Confirm that backup evidence is verified and current before authorizing a release-changing migration.",
|
|
"Verify replacement composition, readiness, queue consumers, and recovery evidence before closing the operation.",
|
|
],
|
|
"limitations": [
|
|
"Drain is observed on the runtime heartbeat interval and does not forcibly terminate active work.",
|
|
"Ops does not create or restore backups and never receives private artifact or key-custody references.",
|
|
"SQLite is a development-only recovery profile; Files objects created before an unhandled process loss may require integrity-scan discovery because the caller-transaction intent was not committed.",
|
|
"A verified receipt proves the recorded drill; it does not make an unsafe post-migration code rollback reversible.",
|
|
],
|
|
"help_contexts": [
|
|
"ops.page.runtime",
|
|
"ops.page.recovery",
|
|
"ops.action.drain-node",
|
|
"ops.state.readiness-blocked",
|
|
"ops.state.stale-node",
|
|
],
|
|
"consequence_classes": {
|
|
"drain_node": "stop routing new work to the selected runtime incarnation while in-flight work completes",
|
|
"cancel_node_drain": "return a still-draining runtime node to active scheduling",
|
|
"inspect_recovery": "read sanitized durable recovery state without replaying or repairing the owning effect",
|
|
},
|
|
},
|
|
),
|
|
),
|
|
route_factory=_route_factory,
|
|
nav_items=(
|
|
NavItem(
|
|
path="/ops",
|
|
label="Ops",
|
|
icon="activity",
|
|
required_any=OPS_READ_SCOPES,
|
|
order=890,
|
|
),
|
|
),
|
|
frontend=FrontendModule(
|
|
module_id="ops",
|
|
package_name="@govoplan/ops-webui",
|
|
routes=(
|
|
FrontendRoute(
|
|
path="/ops",
|
|
component="OpsPage",
|
|
required_any=OPS_READ_SCOPES,
|
|
order=890,
|
|
),
|
|
),
|
|
nav_items=(
|
|
NavItem(
|
|
path="/ops",
|
|
label="Ops",
|
|
icon="activity",
|
|
required_any=OPS_READ_SCOPES,
|
|
order=890,
|
|
),
|
|
),
|
|
view_surfaces=(
|
|
ViewSurface(
|
|
id="ops.navigation",
|
|
module_id="ops",
|
|
kind="navigation",
|
|
label="Operations navigation",
|
|
order=10,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page",
|
|
module_id="ops",
|
|
kind="route",
|
|
label="Operations workspace",
|
|
order=20,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.summary",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Operations summary",
|
|
parent_id="ops.page",
|
|
order=10,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.health",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Health checks",
|
|
parent_id="ops.page",
|
|
order=20,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.runtime",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Runtime cluster",
|
|
parent_id="ops.page",
|
|
order=30,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.recovery",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Recovery evidence",
|
|
parent_id="ops.page",
|
|
order=40,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.governance",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Governance inventory",
|
|
parent_id="ops.page",
|
|
order=50,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.deployment",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Deployment profiles",
|
|
parent_id="ops.page",
|
|
order=60,
|
|
),
|
|
ViewSurface(
|
|
id="ops.page.sizing",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Sizing assumptions",
|
|
parent_id="ops.page",
|
|
order=70,
|
|
),
|
|
ViewSurface(
|
|
id="ops.action.run-probes",
|
|
module_id="ops",
|
|
kind="action",
|
|
label="Run operational probes",
|
|
parent_id="ops.page.health",
|
|
order=80,
|
|
),
|
|
ViewSurface(
|
|
id="ops.action.drain-node",
|
|
module_id="ops",
|
|
kind="action",
|
|
label="Drain runtime node",
|
|
parent_id="ops.page.runtime",
|
|
order=90,
|
|
),
|
|
ViewSurface(
|
|
id="ops.widget.health",
|
|
module_id="ops",
|
|
kind="section",
|
|
label="Operations health widget",
|
|
order=100,
|
|
),
|
|
),
|
|
),
|
|
architecture=ARCHITECTURE,
|
|
)
|
|
|
|
|
|
manifest = with_documentation_structured_translations(
|
|
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
|
|
)
|
|
|
|
|
|
def get_manifest() -> ModuleManifest:
|
|
return manifest
|