Swaps the legacy OpcManager read path for the gateway's SubscriptionEngine over the shared OpcConnectionPool. The OPC client certificate now always comes from MinIO + Mongo (CertProvider, Basic256Sha256) — no more certs/ shipped in the Gitea repository, so a new version branch no longer breaks the reads. - io/slots.py: parse_slot accepts slot v2 (names[]) and converts the legacy v1 (tag_name + topics; cert_path => use_security, path dropped), so gateway and Orchestrator can be deployed in any order. - managers/server_reader.py: new per-server reader with the OpcManager interface the IngestorManager already drives (connect, create_subscription, subscribe, unsubscribe, check_cycles, check_opc_listenning, shutdown). - IngestorManager owns the read path: lazy-MinIO CertProvider, the pool and a read DataSink, built without touching the network. Slots from Redis are normalized on lease and refresh; a malformed slot is notified and the last good config is kept. - GatewayRuntime borrows the pool (writes/gRPC) instead of building it, and no longer closes it on stop: a Postgres/NATS failure can't stop the reads. - pool: pin/unpin per active subscription so release_idle never evicts a subscribed connection; public evict() for lost servers / changed config. - SubscriptionEngine pins on subscribe, unpins on unsubscribe, and drops a subscription locally even when the server-side delete fails. - DataSink: model_id only written when the name has one, keeping v1 documents identical to the legacy ones. - config: CERT_BUCKET default is the legacy streamlit-connectors bucket (D7). - Removes OpcManager, its tests and scripts/generate-and-push-opc-cert.sh; README/CLAUDE.md document the MinIO certificate and the slot v2 format. Tests: 218 unit tests pass; ruff check/format clean. Local end-to-end against an asyncua server: v1 slot with two topics -> one doc per collection, legacy doc shape, idle sweep keeps the subscription, lost-server shutdown and re-initialization work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
2.0 KiB
Python
49 lines
2.0 KiB
Python
"""Gateway-specific env config (NATS/gRPC/certs/write flags).
|
|
|
|
Separate from ingestor.py's inline os.getenv (Redis/Mongo/TTLs) — this module
|
|
covers only the new gateway wiring consumed by app.py in Task 18.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from os import getenv
|
|
|
|
|
|
def _env_bool(name: str, default: bool) -> bool:
|
|
value = getenv(name)
|
|
if value is None:
|
|
return default
|
|
return value.strip().lower() in ('true', '1', 'yes')
|
|
|
|
|
|
@dataclass
|
|
class GatewayConfig:
|
|
nats_url: str = 'nats://localhost:4222'
|
|
nats_write_subject: str = 'opc.write'
|
|
nats_queue: str = 'gateway-writers'
|
|
grpc_enabled: bool = False
|
|
grpc_port: int = 50051
|
|
# Legacy frontend bucket the platform already provisions (SIENTIAPDE-1974 D7);
|
|
# the design's `sientia-dataops` bucket does not exist in the platform MinIO.
|
|
cert_bucket: str = 'streamlit-connectors'
|
|
cert_cache_dir: str = '/tmp/opc-certs' # noqa: S108 # nosec B108 - default is spec'd, overridable via env
|
|
write_timestamp: bool = True
|
|
write_quality: bool = True
|
|
write_min_pause_s: float = 0.0
|
|
idle_conn_max_s: float = 300.0
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'GatewayConfig':
|
|
return cls(
|
|
nats_url=getenv('NATS_URL', cls.nats_url),
|
|
nats_write_subject=getenv('NATS_WRITE_SUBJECT', cls.nats_write_subject),
|
|
nats_queue=getenv('NATS_QUEUE', cls.nats_queue),
|
|
grpc_enabled=_env_bool('GRPC_ENABLED', cls.grpc_enabled),
|
|
grpc_port=int(getenv('GRPC_PORT', cls.grpc_port)),
|
|
cert_bucket=getenv('CERT_BUCKET', cls.cert_bucket),
|
|
cert_cache_dir=getenv('CERT_CACHE_DIR', cls.cert_cache_dir),
|
|
write_timestamp=_env_bool('WRITE_TIMESTAMP', cls.write_timestamp),
|
|
write_quality=_env_bool('WRITE_QUALITY', cls.write_quality),
|
|
write_min_pause_s=float(getenv('WRITE_MIN_PAUSE_S', cls.write_min_pause_s)),
|
|
idle_conn_max_s=float(getenv('IDLE_CONN_MAX_S', cls.idle_conn_max_s)),
|
|
)
|