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.0rc4", "apscheduler<4.0.0", ] Loading src/srm/adapters/database/repos/catalog.py +22 −0 Changes for src/srm/adapters/database/repos/catalog.py: 22 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -74,6 +74,28 @@ class SqlServiceSpecificationRepository(ServiceSpecificationRepository): row = await self._session.scalar(stmt) return ServiceSpecificationMapper.to_domain(row) if row is not None else None async def get_by_ref(self, app_provider_id: str, ref: str) -> ServiceSpecification | None: stmt = ( select(ServiceSpecificationRow) .options( selectinload(ServiceSpecificationRow.deployment_units), selectinload(ServiceSpecificationRow.capability_requirements), ) .where( ServiceSpecificationRow.app_provider_id == app_provider_id, ServiceSpecificationRow.ref == ref, ServiceSpecificationRow.state == ServiceSpecificationState.ACTIVE, ) .order_by( ServiceSpecificationRow.created_at.desc(), ServiceSpecificationRow.version.desc(), ServiceSpecificationRow.id.desc(), ) .limit(1) ) row = await self._session.scalar(stmt) return ServiceSpecificationMapper.to_domain(row) if row is not None else None async def list_by_app_provider_id(self, app_provider_id: str) -> list[ServiceSpecification]: stmt = ( select(ServiceSpecificationRow) Loading 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.0rc4", "apscheduler<4.0.0", ] Loading
src/srm/adapters/database/repos/catalog.py +22 −0 Changes for src/srm/adapters/database/repos/catalog.py: 22 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -74,6 +74,28 @@ class SqlServiceSpecificationRepository(ServiceSpecificationRepository): row = await self._session.scalar(stmt) return ServiceSpecificationMapper.to_domain(row) if row is not None else None async def get_by_ref(self, app_provider_id: str, ref: str) -> ServiceSpecification | None: stmt = ( select(ServiceSpecificationRow) .options( selectinload(ServiceSpecificationRow.deployment_units), selectinload(ServiceSpecificationRow.capability_requirements), ) .where( ServiceSpecificationRow.app_provider_id == app_provider_id, ServiceSpecificationRow.ref == ref, ServiceSpecificationRow.state == ServiceSpecificationState.ACTIVE, ) .order_by( ServiceSpecificationRow.created_at.desc(), ServiceSpecificationRow.version.desc(), ServiceSpecificationRow.id.desc(), ) .limit(1) ) row = await self._session.scalar(stmt) return ServiceSpecificationMapper.to_domain(row) if row is not None else None async def list_by_app_provider_id(self, app_provider_id: str) -> list[ServiceSpecification]: stmt = ( select(ServiceSpecificationRow) Loading