fix(runtime): keep shared artifacts optional
This commit is contained in:
@@ -64,7 +64,10 @@ separate future gate.
|
||||
|
||||
`GET /api/v1/device-plugin-runtimes` exposes the small lifecycle snapshots. The
|
||||
main health response includes ready/total runtime counts as one component beside
|
||||
recording-cache, shared-artifact, media-tool and catalog-reconciler readiness.
|
||||
recording-cache, optional shared-artifact capability, media-tool and
|
||||
catalog-reconciler readiness. A missing optional shared-artifact provider does
|
||||
not degrade the complete local contour.
|
||||
|
||||
The plugin values still do not claim device connectivity, sensor health,
|
||||
process containment or durable supervision.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ Contour health reads only live contracts:
|
||||
- current Mission Runtime state for active device and perception metrics.
|
||||
|
||||
The base health contract now separates local operational readiness from
|
||||
production shared-artifact readiness and reports bounded cache, catalog
|
||||
optional shared-artifact capability and reports bounded cache, catalog
|
||||
reconciler, media-tool and Map Gateway configuration states. It performs no
|
||||
remote fan-out and never invents Worker 006 or device health.
|
||||
|
||||
|
||||
@@ -5,17 +5,18 @@ Status: accepted and implemented
|
||||
|
||||
## Decision
|
||||
|
||||
Mission Core stores portable recorded-session products in a provider-neutral,
|
||||
content-addressed artifact store. The current central provider is the existing
|
||||
Synology SMB mount:
|
||||
Mission Core can exchange portable recorded-session products through an
|
||||
optional provider-neutral, content-addressed artifact gateway. The canonical
|
||||
current operator contour is local and complete without a shared store. An
|
||||
external filesystem provider is an auxiliary transport/prefetch capability,
|
||||
not an operational-readiness dependency and not a production topology
|
||||
decision.
|
||||
|
||||
```text
|
||||
/Volumes/docker/nodedc-mission-core/artifact-store
|
||||
```
|
||||
|
||||
This is product runtime data. It is not a deploy artifact, a Map Gateway cache,
|
||||
a Device Plane package, or a new Docker service. Authentication remains owned
|
||||
by the operating-system SMB mount; Mission Core does not receive a NAS password.
|
||||
The historical RAVNOVES00 migration acceptance used an existing SMB-backed
|
||||
filesystem as one experimental provider. That provider, its mount and its host
|
||||
configuration are outside the current Mission Core runtime contract. Mission
|
||||
Core does not own its authentication, does not require it at startup and does
|
||||
not receive a NAS password.
|
||||
|
||||
Every immutable object is stored under:
|
||||
|
||||
@@ -55,7 +56,7 @@ MISSIONCORE_ARTIFACT_CACHE_MAX_BYTES
|
||||
MISSIONCORE_ARTIFACT_CACHE_FREE_SPACE_RESERVE_BYTES
|
||||
```
|
||||
|
||||
The central root is configured with:
|
||||
An optional shared root is configured explicitly with:
|
||||
|
||||
```text
|
||||
MISSIONCORE_ARTIFACT_STORE_ROOT
|
||||
@@ -115,16 +116,19 @@ When the central root is unavailable:
|
||||
`GET /api/health` exposes the effective shared/standalone mode, central-store
|
||||
availability, bounded CAS and RRD-cache occupancy, media-tool availability and
|
||||
the background catalog reconciler state without disclosing filesystem paths.
|
||||
Operational readiness remains distinct from production shared-artifact
|
||||
readiness so an offline, already pinned field host stays observable rather than
|
||||
being mistaken for a fully connected host.
|
||||
`GET /api/health` reports local operational readiness independently. Optional
|
||||
shared artifacts are exposed as a non-required capability with separate
|
||||
`configured` and `ready` values. Their absence or temporary unavailability does
|
||||
not degrade a healthy local contour. When an operator explicitly configures
|
||||
that capability, its fail-closed cache-miss semantics still apply to artifacts
|
||||
that were expected from the configured gateway.
|
||||
|
||||
## RAVNOVES00 migration acceptance
|
||||
|
||||
The source session is `20260720T065719Z_viewer_live`, displayed as
|
||||
`RAVNOVES00`.
|
||||
|
||||
The accepted central reference currently points to:
|
||||
The reference used by that bounded migration acceptance pointed to:
|
||||
|
||||
```text
|
||||
092f758c09965be98fd76d8191474c528ee20540dce5f4761c2a087f6056a6d5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Operator commands for the Mission Core central artifact gateway."""
|
||||
"""Operator commands for the optional Mission Core artifact gateway."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Content-addressed central artifacts with a bounded, offline-capable host cache."""
|
||||
"""Optional content-addressed artifact exchange with a bounded local cache."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -100,8 +100,9 @@ def build_runtime_readiness(
|
||||
"central_available": None,
|
||||
}
|
||||
shared_artifacts_ready = False
|
||||
shared_artifact_degraded = False
|
||||
shared_artifacts_configured = False
|
||||
else:
|
||||
shared_artifacts_configured = True
|
||||
try:
|
||||
artifact = artifact_gateway.status()
|
||||
cache = artifact.cache
|
||||
@@ -127,7 +128,6 @@ def build_runtime_readiness(
|
||||
},
|
||||
}
|
||||
shared_artifacts_ready = artifact.central_status == "ready"
|
||||
shared_artifact_degraded = not shared_artifacts_ready
|
||||
except (OSError, RuntimeError):
|
||||
artifact_document = {
|
||||
"mode": "shared",
|
||||
@@ -135,7 +135,6 @@ def build_runtime_readiness(
|
||||
"central_available": False,
|
||||
}
|
||||
shared_artifacts_ready = False
|
||||
shared_artifact_degraded = True
|
||||
|
||||
media_tools_ready = ffmpeg_available and ffprobe_available
|
||||
reconciler_document = reconciler.snapshot()
|
||||
@@ -151,11 +150,7 @@ def build_runtime_readiness(
|
||||
and media_tools_ready
|
||||
and reconciler_failures < 3
|
||||
)
|
||||
degraded = (
|
||||
shared_artifact_degraded
|
||||
or reconciler_failures > 0
|
||||
or not operational_ready
|
||||
)
|
||||
degraded = reconciler_failures > 0 or not operational_ready
|
||||
status = (
|
||||
"unavailable"
|
||||
if not operational_ready
|
||||
@@ -170,9 +165,11 @@ def build_runtime_readiness(
|
||||
"version": version,
|
||||
"readiness": {
|
||||
"operational": operational_ready,
|
||||
"production_shared_artifacts": (
|
||||
operational_ready and shared_artifacts_ready
|
||||
),
|
||||
"optional_shared_artifacts": {
|
||||
"required": False,
|
||||
"configured": shared_artifacts_configured,
|
||||
"ready": shared_artifacts_ready,
|
||||
},
|
||||
},
|
||||
"plugin_runtimes": {
|
||||
"ready": ready_runtime_count,
|
||||
|
||||
@@ -57,7 +57,11 @@ def test_runtime_readiness_reports_shared_store_and_bounded_cache(
|
||||
assert document["status"] == "ok"
|
||||
assert document["readiness"] == {
|
||||
"operational": True,
|
||||
"production_shared_artifacts": True,
|
||||
"optional_shared_artifacts": {
|
||||
"required": False,
|
||||
"configured": True,
|
||||
"ready": True,
|
||||
},
|
||||
}
|
||||
components = document["components"]
|
||||
assert isinstance(components, dict)
|
||||
@@ -85,16 +89,47 @@ def test_runtime_readiness_exposes_offline_cache_only_mode(
|
||||
document = _document(tmp_path, gateway=gateway, reconciler=reconciler)
|
||||
|
||||
assert document["ok"] is True
|
||||
assert document["status"] == "degraded"
|
||||
assert document["status"] == "ok"
|
||||
assert document["readiness"] == {
|
||||
"operational": True,
|
||||
"production_shared_artifacts": False,
|
||||
"optional_shared_artifacts": {
|
||||
"required": False,
|
||||
"configured": True,
|
||||
"ready": False,
|
||||
},
|
||||
}
|
||||
components = document["components"]
|
||||
assert isinstance(components, dict)
|
||||
assert components["artifact_store"]["status"] == "offline-cache-only"
|
||||
|
||||
|
||||
def test_runtime_readiness_treats_standalone_as_complete_local_contour(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
reconciler = BackgroundReconcilerReadiness()
|
||||
reconciler.record_success()
|
||||
|
||||
document = _document(tmp_path, gateway=None, reconciler=reconciler)
|
||||
|
||||
assert document["ok"] is True
|
||||
assert document["status"] == "ok"
|
||||
assert document["readiness"] == {
|
||||
"operational": True,
|
||||
"optional_shared_artifacts": {
|
||||
"required": False,
|
||||
"configured": False,
|
||||
"ready": False,
|
||||
},
|
||||
}
|
||||
components = document["components"]
|
||||
assert isinstance(components, dict)
|
||||
assert components["artifact_store"] == {
|
||||
"mode": "standalone",
|
||||
"status": "not-configured",
|
||||
"central_available": None,
|
||||
}
|
||||
|
||||
|
||||
def test_runtime_readiness_fails_after_repeated_reconciler_errors(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user