mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-11 11:28:51 +00:00
fix(repository): idempotent sentinel agent creation via ON CONFLICT (M-6)
Sentinel agents (server-scanner, cloud-aws-sm, cloud-azure-kv, cloud-gcp-sm) were created on startup with a plain INSERT whose duplicate-key error was swallowed unconditionally. That silenced every other DB failure too (connectivity drop, permissions change, unrelated constraint violation) — a restart after the first boot quietly de-fanged cloud discovery and the network scanner (CWE-662, CWE-209- adjacent). Shape A: add AgentRepository.CreateIfNotExists using ON CONFLICT (id) DO NOTHING RETURNING id + sql.ErrNoRows discrimination. This keeps the strict Create semantics (duplicate-key is an error) intact for real agent registration and gives sentinels their own idempotent path. - repo: CreateIfNotExists returns (created bool, err error); false,nil on pre-existing row; false,wrapped err on anything else. - interface: CreateIfNotExists added to AgentRepository. - main.go: 4 sentinel sites log Error/Info/Debug distinctly. - mocks: service + integration mocks implement the new method. - tests: 4 new testcontainers integration tests cover first-insert, idempotent second-call, concurrent 16-goroutine race (exactly one creator, no duplicate-key panic), and pre-cancelled context surfacing. Coverage gates (go test -cover): service 67.6%/55, handler 78.6%/60, domain 92.7%/40, middleware 80.0%/30, crypto 86.7%/85. Race/vet/ golangci-lint v2.11.4 (0 issues)/govulncheck v1.2.0 clean across all touched packages.
This commit is contained in:
@@ -90,8 +90,18 @@ type AgentRepository interface {
|
||||
List(ctx context.Context) ([]*domain.Agent, error)
|
||||
// Get retrieves an agent by ID.
|
||||
Get(ctx context.Context, id string) (*domain.Agent, error)
|
||||
// Create stores a new agent.
|
||||
// Create stores a new agent. Callers that want duplicate-key errors surfaced
|
||||
// (e.g. real-agent registration) must use this method; sentinel/bootstrap
|
||||
// paths that expect the row to already exist on restart should call
|
||||
// CreateIfNotExists instead (M-6, CWE-662).
|
||||
Create(ctx context.Context, agent *domain.Agent) error
|
||||
// CreateIfNotExists creates an agent only if the ID doesn't already exist
|
||||
// (INSERT ... ON CONFLICT (id) DO NOTHING). Returns true if the row was
|
||||
// newly inserted, false if a row with the same ID already existed. Used
|
||||
// by the sentinel-agent bootstrap path in cmd/server/main.go so restarts
|
||||
// and upgrades are idempotent without swallowing unrelated database
|
||||
// failures (M-6, CWE-662).
|
||||
CreateIfNotExists(ctx context.Context, agent *domain.Agent) (bool, error)
|
||||
// Update modifies an existing agent.
|
||||
Update(ctx context.Context, agent *domain.Agent) error
|
||||
// Delete removes an agent.
|
||||
|
||||
Reference in New Issue
Block a user