bda568b25c
Go service for Proxmox homelab cluster provisioning. Handles PXE boot, Proxmox autoinstall (answer file generation), cluster join via SSH, and Infrastructure API registration. - Host state machine (registered → pxe_ready → installing → ready) - dnsmasq supervisor with MAC-based allowlist - iPXE script and Proxmox answer file generation - First-boot phone-home → cluster join → infra registration - Operation locking with expiry (409 on conflict) - SSE event hub for real-time dashboard updates - Admin dashboard (host grid, detail, registration form) - Config-driven server types with hot-reload - Docker deployment (multi-stage fat image) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
type Locks struct {
|
|
DB *sql.DB
|
|
TTLMinutes int
|
|
}
|
|
|
|
func (s *Locks) Acquire(ctx context.Context, hostID, operationID int64) error {
|
|
s.cleanExpired(ctx)
|
|
expiresAt := time.Now().UTC().Add(time.Duration(s.TTLMinutes) * time.Minute).Format(time.RFC3339)
|
|
_, err := s.DB.ExecContext(ctx, `
|
|
INSERT INTO operation_locks(host_id, operation_id, expires_at)
|
|
VALUES(?,?,?)
|
|
`, hostID, operationID, expiresAt)
|
|
if err != nil {
|
|
return fmt.Errorf("acquire lock: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Locks) Release(ctx context.Context, hostID int64) error {
|
|
_, err := s.DB.ExecContext(ctx, `DELETE FROM operation_locks WHERE host_id = ?`, hostID)
|
|
return err
|
|
}
|
|
|
|
func (s *Locks) IsLocked(ctx context.Context, hostID int64) (bool, error) {
|
|
s.cleanExpired(ctx)
|
|
var count int
|
|
err := s.DB.QueryRowContext(ctx, `SELECT COUNT(1) FROM operation_locks WHERE host_id = ?`, hostID).Scan(&count)
|
|
if err != nil {
|
|
return false, fmt.Errorf("check lock: %w", err)
|
|
}
|
|
return count > 0, nil
|
|
}
|
|
|
|
func (s *Locks) cleanExpired(ctx context.Context) {
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
_, _ = s.DB.ExecContext(ctx, `DELETE FROM operation_locks WHERE expires_at < ?`, now)
|
|
}
|