Loading .env.example +3 −0 Changes for .env.example: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -15,3 +15,6 @@ NATS_SETTINGS__DRAIN_TIMEOUT = 30 FEDERATION_MANAGER_SETTINGS__BASE_URL = "http://localhost:8082" FEDERATION_MANAGER_SETTINGS__TIMEOUT = 0.5 FEDERATION_MANAGER_SETTINGS__ENABLED = true # One complete SDK bootstrap; leave unset for an empty topology/health-only startup. # BOOTSTRAP_TOPOLOGY_PATH=/etc/srm/topology.yaml .pre-commit-config.yaml +1 −0 Changes for .pre-commit-config.yaml: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -35,3 +35,4 @@ repos: - "sqlalchemy>=2.0.48" - "structlog>=25.5.0" - "testcontainers[postgres]>=4.13.2" - "types-PyYAML>=6.0.12" README.md +30 −0 Changes for README.md: 30 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,3 +4,33 @@ A Python, FastAPI-based microservice to work as the brains of ETSI SDG OpenOP. ## WIP WIP ## Release 2 control-path bootstrap Set `BOOTSTRAP_TOPOLOGY_PATH` to one mounted, complete SDK bootstrap document. SRM loads it once through `BootstrapTopology`, builds the runtime registry from its operational entries, and passes the same object through the existing four- method discovery bridge. The SDK owns parsing and adapter-kind construction. SRM keeps topology persistence, selection and placement. Declare identity once in `control_paths[].ref`. Registry construction derives `config["control_path_ref"]` from that key on a copied config. SDK execution methods reject another target with `INVALID_TARGET_REF` before credentials or backend calls. Duplicate registry refs and conflicting config identities fail before any instance is constructed. Without `BOOTSTRAP_TOPOLOGY_PATH`, the existing empty-topology health startup remains available. Existing create-missing topology seeding is unchanged; restarting does not reconcile edits to existing database records. Use SDK 2.0.0rc3 with the matching chart/configuration migration. Kubernetes paths require explicit kubeconfig/context/namespace or explicit in-cluster mode and namespace. Deployment tooling owns protected credential mounts and the writable SDK runtime directory. See `tests/fixtures/tf_sdk_bootstrap_example.yaml`. SDK status observation and SRM termination behavior remain unchanged. Broader UNKNOWN handling remains a separate follow-up. The SDK artifact and matching component revisions must be available before merging/deploying this change. Upgrade or roll back SDK, SRM and deployment configuration together. pyproject.toml +1 −1 Changes for pyproject.toml: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ dependencies = [ "sqlalchemy>=2.0.48", "asyncpg>=0.31.0", "nats-py>=2.10.0", "openop-tfsdk==2.0.0rc2", "openop-tfsdk==2.0.0rc3", "apscheduler<4.0.0", ] Loading src/srm/adapters/transformation_functions/registry.py +16 −2 Changes for src/srm/adapters/transformation_functions/registry.py: 16 added lines, 2 removed lines. Original line number Diff line number Diff line from collections.abc import Mapping from copy import deepcopy import structlog Loading Loading @@ -35,7 +36,7 @@ def build_control_path_registry( settings: ControlPathSettings, factory: TransformationFunctionAdapterFactory | None, ) -> ConfiguredControlPathRegistry: logger.info("building_cpr", entries=settings.entries) logger.info("building_cpr", entry_count=len(settings.entries)) if not settings.entries: return ConfiguredControlPathRegistry({}) if factory is None: Loading @@ -43,9 +44,22 @@ def build_control_path_registry( "control paths are configured but no TF-SDK adapter factory is installed: " + ", ".join(entry.control_path_ref for entry in settings.entries) ) refs: set[str] = set() for entry in settings.entries: if not entry.control_path_ref.strip() or entry.control_path_ref in refs: raise ValueError("Control-path refs must be nonempty and unique") refs.add(entry.control_path_ref) if ( "control_path_ref" in entry.config and entry.config["control_path_ref"] != entry.control_path_ref ): raise ValueError("Control-path config identity conflicts with its registry key") return ConfiguredControlPathRegistry( { entry.control_path_ref: factory.create_adapter(entry.adapter, entry.config) entry.control_path_ref: factory.create_adapter( entry.adapter, {**deepcopy(entry.config), "control_path_ref": entry.control_path_ref}, ) for entry in settings.entries } ) Loading
.env.example +3 −0 Changes for .env.example: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -15,3 +15,6 @@ NATS_SETTINGS__DRAIN_TIMEOUT = 30 FEDERATION_MANAGER_SETTINGS__BASE_URL = "http://localhost:8082" FEDERATION_MANAGER_SETTINGS__TIMEOUT = 0.5 FEDERATION_MANAGER_SETTINGS__ENABLED = true # One complete SDK bootstrap; leave unset for an empty topology/health-only startup. # BOOTSTRAP_TOPOLOGY_PATH=/etc/srm/topology.yaml
.pre-commit-config.yaml +1 −0 Changes for .pre-commit-config.yaml: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -35,3 +35,4 @@ repos: - "sqlalchemy>=2.0.48" - "structlog>=25.5.0" - "testcontainers[postgres]>=4.13.2" - "types-PyYAML>=6.0.12"
README.md +30 −0 Changes for README.md: 30 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,3 +4,33 @@ A Python, FastAPI-based microservice to work as the brains of ETSI SDG OpenOP. ## WIP WIP ## Release 2 control-path bootstrap Set `BOOTSTRAP_TOPOLOGY_PATH` to one mounted, complete SDK bootstrap document. SRM loads it once through `BootstrapTopology`, builds the runtime registry from its operational entries, and passes the same object through the existing four- method discovery bridge. The SDK owns parsing and adapter-kind construction. SRM keeps topology persistence, selection and placement. Declare identity once in `control_paths[].ref`. Registry construction derives `config["control_path_ref"]` from that key on a copied config. SDK execution methods reject another target with `INVALID_TARGET_REF` before credentials or backend calls. Duplicate registry refs and conflicting config identities fail before any instance is constructed. Without `BOOTSTRAP_TOPOLOGY_PATH`, the existing empty-topology health startup remains available. Existing create-missing topology seeding is unchanged; restarting does not reconcile edits to existing database records. Use SDK 2.0.0rc3 with the matching chart/configuration migration. Kubernetes paths require explicit kubeconfig/context/namespace or explicit in-cluster mode and namespace. Deployment tooling owns protected credential mounts and the writable SDK runtime directory. See `tests/fixtures/tf_sdk_bootstrap_example.yaml`. SDK status observation and SRM termination behavior remain unchanged. Broader UNKNOWN handling remains a separate follow-up. The SDK artifact and matching component revisions must be available before merging/deploying this change. Upgrade or roll back SDK, SRM and deployment configuration together.
pyproject.toml +1 −1 Changes for pyproject.toml: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ dependencies = [ "sqlalchemy>=2.0.48", "asyncpg>=0.31.0", "nats-py>=2.10.0", "openop-tfsdk==2.0.0rc2", "openop-tfsdk==2.0.0rc3", "apscheduler<4.0.0", ] Loading
src/srm/adapters/transformation_functions/registry.py +16 −2 Changes for src/srm/adapters/transformation_functions/registry.py: 16 added lines, 2 removed lines. Original line number Diff line number Diff line from collections.abc import Mapping from copy import deepcopy import structlog Loading Loading @@ -35,7 +36,7 @@ def build_control_path_registry( settings: ControlPathSettings, factory: TransformationFunctionAdapterFactory | None, ) -> ConfiguredControlPathRegistry: logger.info("building_cpr", entries=settings.entries) logger.info("building_cpr", entry_count=len(settings.entries)) if not settings.entries: return ConfiguredControlPathRegistry({}) if factory is None: Loading @@ -43,9 +44,22 @@ def build_control_path_registry( "control paths are configured but no TF-SDK adapter factory is installed: " + ", ".join(entry.control_path_ref for entry in settings.entries) ) refs: set[str] = set() for entry in settings.entries: if not entry.control_path_ref.strip() or entry.control_path_ref in refs: raise ValueError("Control-path refs must be nonempty and unique") refs.add(entry.control_path_ref) if ( "control_path_ref" in entry.config and entry.config["control_path_ref"] != entry.control_path_ref ): raise ValueError("Control-path config identity conflicts with its registry key") return ConfiguredControlPathRegistry( { entry.control_path_ref: factory.create_adapter(entry.adapter, entry.config) entry.control_path_ref: factory.create_adapter( entry.adapter, {**deepcopy(entry.config), "control_path_ref": entry.control_path_ref}, ) for entry in settings.entries } )