Files
cloud-ip-validator/internal/db/bootstrap.go
T

123 lines
3.8 KiB
Go

package db
import (
"context"
"fmt"
"cloudipvalidator/internal/config"
)
// BootstrapFromConfig ensures the database's baseline state matches the
// YAML config on a fresh install, then gets out of the way.
//
// - ip_addresses -> SeedQueue: unchanged, always additive. This is a
// separate concept from the runtime admin queue API (SubmitIPs) — every
// control-api startup re-adds any address from cfg.IPAddresses that
// isn't already in the queue.
// - validators / sites / target_groups / check_types: applied from YAML
// only if the corresponding table is currently empty. Once any row
// exists (via this bootstrap or the admin API), YAML for that section
// is ignored on every subsequent startup — the database is the source
// of truth from that point on. This is what makes admin API changes to
// these four entities survive a restart.
func (d *DB) BootstrapFromConfig(ctx context.Context, cfg *config.ControlAPI) error {
if err := d.bootstrapValidators(ctx, cfg.Validators); err != nil {
return fmt.Errorf("bootstrap validators: %w", err)
}
if err := d.bootstrapSites(ctx, cfg.Sites); err != nil {
return fmt.Errorf("bootstrap sites: %w", err)
}
if err := d.bootstrapTargetGroups(ctx, cfg.Targets); err != nil {
return fmt.Errorf("bootstrap target groups: %w", err)
}
if err := d.bootstrapCheckTypes(ctx, cfg.CheckTypes); err != nil {
return fmt.Errorf("bootstrap check types: %w", err)
}
if err := d.bootstrapSettings(ctx, cfg.Orchestrator.FIPSettleSeconds); err != nil {
return fmt.Errorf("bootstrap settings: %w", err)
}
if err := d.SeedQueue(ctx, cfg.IPAddresses); err != nil {
return fmt.Errorf("seed ip queue: %w", err)
}
return nil
}
func (d *DB) bootstrapValidators(ctx context.Context, validators []config.ValidatorConfig) error {
var count int
if err := d.QueryRowContext(ctx, `SELECT COUNT(*) FROM validators`).Scan(&count); err != nil {
return err
}
if count > 0 {
return nil
}
for _, v := range validators {
if err := d.RegisterValidator(ctx, v.ValidatorID, "", v.OSPortID, ""); err != nil {
return fmt.Errorf("seed validator %s: %w", v.ValidatorID, err)
}
}
return nil
}
func (d *DB) bootstrapSites(ctx context.Context, sites []config.SiteConfig) error {
var count int
if err := d.QueryRowContext(ctx, `SELECT COUNT(*) FROM sites`).Scan(&count); err != nil {
return err
}
if count > 0 {
return nil
}
for _, s := range sites {
if err := d.UpsertSite(ctx, s.Index, s.SiteID); err != nil {
return fmt.Errorf("seed site %s: %w", s.SiteID, err)
}
}
return nil
}
func (d *DB) bootstrapTargetGroups(ctx context.Context, targets map[string][]string) error {
var count int
if err := d.QueryRowContext(ctx, `SELECT COUNT(*) FROM target_groups`).Scan(&count); err != nil {
return err
}
if count > 0 {
return nil
}
for name, addrs := range targets {
if err := d.UpsertTargetGroup(ctx, name, addrs); err != nil {
return fmt.Errorf("seed target group %s: %w", name, err)
}
}
return nil
}
func (d *DB) bootstrapCheckTypes(ctx context.Context, checkTypes []config.CheckTypeConfig) error {
var count int
if err := d.QueryRowContext(ctx, `SELECT COUNT(*) FROM check_types`).Scan(&count); err != nil {
return err
}
if count > 0 {
return nil
}
for _, ct := range checkTypes {
if err := d.UpsertCheckType(ctx, ct.Name, ct.Enabled, ct.Targets); err != nil {
return fmt.Errorf("seed check type %s: %w", ct.Name, err)
}
}
return nil
}
func (d *DB) bootstrapSettings(ctx context.Context, fipSettleSeconds int) error {
var count int
if err := d.QueryRowContext(ctx, `SELECT COUNT(*) FROM settings`).Scan(&count); err != nil {
return err
}
if count > 0 {
return nil
}
now := timeToDB(Now())
_, err := d.ExecContext(ctx, `
INSERT INTO settings (id, fip_settle_seconds, created_at, updated_at) VALUES (1, ?, ?, ?)
`, fipSettleSeconds, now, now)
return err
}