Enforce at most one managed source globally#719
Conversation
The config file validation already limited managed sources to one, but the API creation path did not enforce this invariant. Add a check inside the `CreateSource` serializable transaction that queries `GetManagedSources` and rejects the request with HTTP 409 when a managed source already exists (regardless of creation type). Handle PostgreSQL serialization failures (`40001`) from concurrent races as the same error. Guard the config-load path as well: `Initialize` now calls `checkManagedSourceLimit` before upserting CONFIG sources, rejecting startup when the config introduces a managed source while an API-created one already exists in the database. Strip the existing managed source name from the client-facing error message to avoid information leakage; the name is logged server-side instead. Update existing tests that created multiple managed sources to use file-data sources, and add new integration and API-layer tests for the limit. Update the smoke-test skill to cover the 409 scenario and use file-data sources for list-completeness checks. Fixes #718 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementation PlanProblem StatementThe config file validation enforces that at most one managed source can exist ( Design Decisions
Changes by File
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #719 +/- ##
==========================================
- Coverage 60.73% 60.66% -0.08%
==========================================
Files 107 107
Lines 10404 10445 +41
==========================================
+ Hits 6319 6336 +17
- Misses 3539 3561 +22
- Partials 546 548 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The `TestAuthzIntegration_SourceCRUD` test creates a managed source via
the API while the base config already provides one ("internal"). Switch
the test to use file-data sources since it tests CRUD authorization, not
managed source behavior.
Fixes CI failure introduced by the managed source limit enforcement.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run `task docs` to update the generated OpenAPI spec after adding the `@Failure 409` annotation to the upsert source handler. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `examples/config-docker-with-managed.yaml` example was added with a `format: upstream` field that is being removed by #724 (drop ToolHive registry format support). Drop the field here to avoid a merge conflict with that branch — regardless of merge order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Grype vulnerability DB added `GHSA-pc3f-x583-g7j2` (High) against v0.5.0 between this PR's previous CI run and now, which broke the Security Scan step. v0.5.1 is the fix. Pulled in transitively via `k8s.io/client-go`; no direct callers in this repo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
CreateSourceinside its serializable transaction: queryGetManagedSourcesand return HTTP 409 when one already existsInitialize(config reload): reject startup when config introduces a managed source while an API-created one exists in the DB40001) from concurrent races asErrManagedSourceLimitReachedinstead of a vague 500@Failure 409annotation to the upsert source handlerTest plan
task buildpassestask lint-fix— 0 issuesgo test ./internal/api/v1/— all passgo test ./internal/service/db/— all pass (including newTestCreateSource_ManagedLimitReached,TestCreateSource_NonManagedAllowedWhenManagedExists,TestInitialize_RejectsConfigManagedWhenAPIManagedExists)Fixes #718
🤖 Generated with Claude Code