mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 12:01:13 +00:00
[AZ-319] C11 HttpTileUploader (post-landing upload path)
Lands the production HttpTileUploader composing AZ-317's gate, AZ-318's per-flight signing, and consumer-side cuts over c6 storage. Implements the full upload flow: gate ON_GROUND -> start_session -> enumerate pending -> per-batch multipart POST with Ed25519 signing -> mark_uploaded on ack -> end_session in finally. Honours Retry-After (RFC 7231 int + HTTP-date), exponential backoff on 5xx, fail-fast on TLS/401/403. Adds C11Config block, three FDR kinds (tile.queued, tile.rejected, batch.complete), and the build_tile_uploader composition-root factory. Cross-component access to c6 stays Protocol-cut (AZ-507 / AZ-270). Tests: 17 new unit tests covering AC-1..AC-14 plus throughput NFR; AZ-272 schema fixtures for the three new FDR kinds. Full unit suite: 1404 passed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""C11 TileManager composition-root factories (AZ-317, AZ-318).
|
||||
"""C11 TileManager composition-root factories (AZ-317, AZ-318, AZ-319).
|
||||
|
||||
Wires the upload-side services that have landed:
|
||||
|
||||
@@ -8,6 +8,10 @@ Wires the upload-side services that have landed:
|
||||
* :func:`build_per_flight_key_manager` (AZ-318) — wires the AZ-273
|
||||
:class:`FdrClient` and the project ``Clock`` strategy into the
|
||||
ephemeral signing-key manager.
|
||||
* :func:`build_tile_uploader` (AZ-319) — composes the gate, the
|
||||
key manager, the c6 storage cuts, an :class:`httpx.Client`, and
|
||||
the :class:`C11Config` block into the production
|
||||
:class:`HttpTileUploader`.
|
||||
|
||||
Composition root is the ONLY layer permitted to import from
|
||||
``components.c11_tile_manager`` (per ``module-layout.md`` Rule 9 +
|
||||
@@ -16,13 +20,18 @@ the AZ-270 lint).
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import httpx
|
||||
|
||||
from gps_denied_onboard.components.c11_tile_manager import (
|
||||
C11Config,
|
||||
FlightStateGate,
|
||||
FlightStateSource,
|
||||
HttpTileUploader,
|
||||
PerFlightKeyManager,
|
||||
)
|
||||
from gps_denied_onboard.config.schema import ConfigError
|
||||
from gps_denied_onboard.fdr_client import FdrClient, make_fdr_client
|
||||
from gps_denied_onboard.logging import get_logger
|
||||
|
||||
@@ -33,12 +42,15 @@ if TYPE_CHECKING:
|
||||
__all__ = [
|
||||
"build_flight_state_gate",
|
||||
"build_per_flight_key_manager",
|
||||
"build_tile_uploader",
|
||||
]
|
||||
|
||||
|
||||
_C11_GATE_LOGGER = "c11_tile_manager.flight_state_gate"
|
||||
_C11_SIGNING_LOGGER = "c11_tile_manager.signing_key"
|
||||
_C11_SIGNING_PRODUCER_ID = "c11_tile_manager.signing_key"
|
||||
_C11_UPLOADER_LOGGER = "c11_tile_manager.tile_uploader"
|
||||
_C11_UPLOADER_PRODUCER_ID = "c11_tile_manager.tile_uploader"
|
||||
|
||||
|
||||
def build_flight_state_gate(*, source: FlightStateSource) -> FlightStateGate:
|
||||
@@ -76,3 +88,60 @@ def build_per_flight_key_manager(
|
||||
logger=logger,
|
||||
clock=clock,
|
||||
)
|
||||
|
||||
|
||||
def build_tile_uploader(
|
||||
config: Config,
|
||||
*,
|
||||
http_client: httpx.Client,
|
||||
tile_store: Any,
|
||||
tile_metadata_store: Any,
|
||||
flight_state_gate: FlightStateGate,
|
||||
key_manager: PerFlightKeyManager,
|
||||
fdr_client: FdrClient | None = None,
|
||||
) -> HttpTileUploader:
|
||||
"""Construct a wired :class:`HttpTileUploader` (AZ-319).
|
||||
|
||||
The c6 surfaces (``tile_store``, ``tile_metadata_store``) are
|
||||
consumer-side cuts injected here by the operator-binary
|
||||
composition root; C11 NEVER imports c6 directly. The ``http_client``
|
||||
is also caller-owned: production wiring uses one long-lived
|
||||
:class:`httpx.Client` per process; tests inject
|
||||
``httpx.Client(transport=httpx.MockTransport(...))``.
|
||||
"""
|
||||
|
||||
block = config.components.get("c11_tile_manager")
|
||||
if block is None:
|
||||
raise ConfigError(
|
||||
"build_tile_uploader: config.components['c11_tile_manager'] "
|
||||
"block is missing — register C11Config and supply YAML"
|
||||
)
|
||||
if not isinstance(block, C11Config):
|
||||
raise ConfigError(
|
||||
"build_tile_uploader: config.components['c11_tile_manager'] "
|
||||
f"must be a C11Config, got {type(block).__name__}"
|
||||
)
|
||||
if not block.satellite_provider_ingest_url:
|
||||
raise ConfigError(
|
||||
"build_tile_uploader: C11Config.satellite_provider_ingest_url "
|
||||
"must be configured for production / operator wiring"
|
||||
)
|
||||
if not block.companion_id:
|
||||
raise ConfigError(
|
||||
"build_tile_uploader: C11Config.companion_id must be set "
|
||||
"(stable per-companion identifier for the parent-suite "
|
||||
"voting layer)"
|
||||
)
|
||||
if fdr_client is None:
|
||||
fdr_client = make_fdr_client(_C11_UPLOADER_PRODUCER_ID, config)
|
||||
logger = get_logger(_C11_UPLOADER_LOGGER)
|
||||
return HttpTileUploader(
|
||||
http_client=http_client,
|
||||
tile_store=tile_store,
|
||||
tile_metadata_store=tile_metadata_store,
|
||||
flight_state_gate=flight_state_gate,
|
||||
key_manager=key_manager,
|
||||
fdr_client=fdr_client,
|
||||
logger=logger,
|
||||
config=block,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user