feat(workload): write-through workload sync + boot-time backfill

CRUD on Project / Stack / StaticSite now keeps a paired Workload
row in sync. Secret setters (webhook secret, signing secret,
require-signature toggle, notification secret) all re-sync after
mutating the source-of-truth row so the workload row always
reflects the canonical state.

Delete cascades: DeleteProject/Stack/StaticSite now drop the
matching workload row plus any container index entries owned by
it, so global views don't show ghost rows.

Boot-time BackfillWorkloads scans every project/stack/site and
ensures each has a workload row. Idempotent — safe to run on
every restart, recovers from a deleted/missing workload row.

Behavior unchanged for existing call sites; the workloads table
just starts being populated. Deployer / reconciler / consumer
switchover land in the next commit.
This commit is contained in:
2026-05-09 13:28:20 +03:00
parent f54a6ecee3
commit db235c1412
7 changed files with 410 additions and 18 deletions
+13 -1
View File
@@ -29,6 +29,9 @@ func (s *Store) CreateStack(st Stack) (Stack, error) {
if err != nil {
return Stack{}, fmt.Errorf("insert stack: %w", err)
}
if err := s.SyncStackWorkload(st); err != nil {
return Stack{}, fmt.Errorf("sync stack workload: %w", err)
}
return st, nil
}
@@ -79,7 +82,7 @@ func (s *Store) UpdateStack(st Stack) error {
if n == 0 {
return fmt.Errorf("stack %s: %w", st.ID, ErrNotFound)
}
return nil
return s.SyncStackWorkload(st)
}
// UpdateStackStatus updates the deployment status + error fields.
@@ -117,6 +120,7 @@ func (s *Store) SetStackCurrentRevision(id, revisionID string) error {
}
// DeleteStack removes a stack by ID. Cascading deletes handle revisions + deploys.
// Workload row + container index entries are removed too.
func (s *Store) DeleteStack(id string) error {
result, err := s.db.Exec(`DELETE FROM stacks WHERE id = ?`, id)
if err != nil {
@@ -126,6 +130,14 @@ func (s *Store) DeleteStack(id string) error {
if n == 0 {
return fmt.Errorf("stack %s: %w", id, ErrNotFound)
}
if w, err := s.GetWorkloadByRef(WorkloadKindStack, id); err == nil {
if err := s.DeleteContainersByWorkload(w.ID); err != nil {
return fmt.Errorf("delete stack containers: %w", err)
}
if err := s.DeleteWorkload(w.ID); err != nil {
return fmt.Errorf("delete stack workload: %w", err)
}
}
return nil
}