Initial implementation: host lifecycle + PXE + admin dashboard
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>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user