db235c1412
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.
120 lines
4.0 KiB
Go
120 lines
4.0 KiB
Go
package store
|
|
|
|
import "fmt"
|
|
|
|
// SyncProjectWorkload upserts the Workload row paired with a project so that
|
|
// its name, notification config, and webhook secrets stay in sync. Called from
|
|
// CreateProject / UpdateProject / SetProject*Secret paths. Idempotent — safe
|
|
// to call when a workload row already exists for the (project, RefID) pair.
|
|
func (s *Store) SyncProjectWorkload(p Project) error {
|
|
existing, err := s.GetWorkloadByRef(WorkloadKindProject, p.ID)
|
|
if err == nil {
|
|
existing.Name = p.Name
|
|
existing.NotificationURL = p.NotificationURL
|
|
existing.NotificationSecret = p.NotificationSecret
|
|
existing.WebhookSecret = p.WebhookSecret
|
|
existing.WebhookSigningSecret = p.WebhookSigningSecret
|
|
existing.WebhookRequireSignature = p.WebhookRequireSignature
|
|
return s.UpdateWorkload(existing)
|
|
}
|
|
_, err = s.CreateWorkload(Workload{
|
|
Kind: string(WorkloadKindProject),
|
|
RefID: p.ID,
|
|
Name: p.Name,
|
|
NotificationURL: p.NotificationURL,
|
|
NotificationSecret: p.NotificationSecret,
|
|
WebhookSecret: p.WebhookSecret,
|
|
WebhookSigningSecret: p.WebhookSigningSecret,
|
|
WebhookRequireSignature: p.WebhookRequireSignature,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("create project workload: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SyncStackWorkload upserts the Workload row paired with a stack. Stacks
|
|
// don't (yet) carry their own notification or webhook config — those fields
|
|
// stay empty on the workload row until the stack model gains them.
|
|
func (s *Store) SyncStackWorkload(st Stack) error {
|
|
existing, err := s.GetWorkloadByRef(WorkloadKindStack, st.ID)
|
|
if err == nil {
|
|
existing.Name = st.Name
|
|
return s.UpdateWorkload(existing)
|
|
}
|
|
_, err = s.CreateWorkload(Workload{
|
|
Kind: string(WorkloadKindStack),
|
|
RefID: st.ID,
|
|
Name: st.Name,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("create stack workload: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SyncStaticSiteWorkload upserts the Workload row paired with a static site.
|
|
func (s *Store) SyncStaticSiteWorkload(site StaticSite) error {
|
|
existing, err := s.GetWorkloadByRef(WorkloadKindSite, site.ID)
|
|
if err == nil {
|
|
existing.Name = site.Name
|
|
existing.NotificationURL = site.NotificationURL
|
|
existing.NotificationSecret = site.NotificationSecret
|
|
existing.WebhookSecret = site.WebhookSecret
|
|
existing.WebhookSigningSecret = site.WebhookSigningSecret
|
|
existing.WebhookRequireSignature = site.WebhookRequireSignature
|
|
return s.UpdateWorkload(existing)
|
|
}
|
|
_, err = s.CreateWorkload(Workload{
|
|
Kind: string(WorkloadKindSite),
|
|
RefID: site.ID,
|
|
Name: site.Name,
|
|
NotificationURL: site.NotificationURL,
|
|
NotificationSecret: site.NotificationSecret,
|
|
WebhookSecret: site.WebhookSecret,
|
|
WebhookSigningSecret: site.WebhookSigningSecret,
|
|
WebhookRequireSignature: site.WebhookRequireSignature,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("create static site workload: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BackfillWorkloads scans every project / stack / static_site row and ensures
|
|
// each has a matching workload row. Called once at boot before HTTP starts so
|
|
// any pre-Workload-refactor data is upgraded transparently. Idempotent.
|
|
func (s *Store) BackfillWorkloads() error {
|
|
projects, err := s.GetAllProjects()
|
|
if err != nil {
|
|
return fmt.Errorf("backfill: list projects: %w", err)
|
|
}
|
|
for _, p := range projects {
|
|
if err := s.SyncProjectWorkload(p); err != nil {
|
|
return fmt.Errorf("backfill project %s: %w", p.ID, err)
|
|
}
|
|
}
|
|
|
|
stacks, err := s.GetAllStacks()
|
|
if err != nil {
|
|
return fmt.Errorf("backfill: list stacks: %w", err)
|
|
}
|
|
for _, st := range stacks {
|
|
if err := s.SyncStackWorkload(st); err != nil {
|
|
return fmt.Errorf("backfill stack %s: %w", st.ID, err)
|
|
}
|
|
}
|
|
|
|
sites, err := s.GetAllStaticSites()
|
|
if err != nil {
|
|
return fmt.Errorf("backfill: list static sites: %w", err)
|
|
}
|
|
for _, site := range sites {
|
|
if err := s.SyncStaticSiteWorkload(site); err != nil {
|
|
return fmt.Errorf("backfill static site %s: %w", site.ID, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|