Files
drjones 3678b199d0
Some checks failed
Test / test (push) Has been cancelled
Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
2026-07-04 09:31:23 +00:00

128 lines
2.6 KiB
Go

package fleet
import (
"sync"
"time"
"github.com/google/uuid"
)
// BatchJob tracks a crucible batch dispatch.
type BatchJob struct {
ID string `json:"id"`
Command string `json:"command"`
HostIDs []string `json:"host_ids"`
Results []BatchResult `json:"results"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
// BatchResult is one host outcome in a batch job.
type BatchResult struct {
HostID string `json:"host_id"`
Hostname string `json:"hostname,omitempty"`
Status string `json:"status"`
Message string `json:"message,omitempty"`
CommandID string `json:"command_id,omitempty"`
}
// CrucibleStore holds in-memory batch job history.
type CrucibleStore struct {
mu sync.RWMutex
jobs map[string]*BatchJob
max int
}
func NewCrucibleStore(maxHistory int) *CrucibleStore {
if maxHistory <= 0 {
maxHistory = 100
}
return &CrucibleStore{
jobs: make(map[string]*BatchJob),
max: maxHistory,
}
}
func (c *CrucibleStore) Create(command string, hostIDs []string) *BatchJob {
job := &BatchJob{
ID: uuid.NewString(),
Command: command,
HostIDs: append([]string(nil), hostIDs...),
Results: make([]BatchResult, 0, len(hostIDs)),
Status: "running",
CreatedAt: time.Now().UTC(),
}
c.mu.Lock()
c.jobs[job.ID] = job
c.trimLocked()
c.mu.Unlock()
return job
}
func (c *CrucibleStore) Get(id string) (*BatchJob, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
job, ok := c.jobs[id]
return job, ok
}
func (c *CrucibleStore) AddResult(jobID string, result BatchResult) {
c.mu.Lock()
defer c.mu.Unlock()
job, ok := c.jobs[jobID]
if !ok {
return
}
job.Results = append(job.Results, result)
}
func (c *CrucibleStore) Complete(jobID, status string) {
c.mu.Lock()
defer c.mu.Unlock()
if job, ok := c.jobs[jobID]; ok {
job.Status = status
}
}
func (c *CrucibleStore) History(limit int) []*BatchJob {
if limit <= 0 {
limit = 20
}
c.mu.RLock()
defer c.mu.RUnlock()
jobs := make([]*BatchJob, 0, len(c.jobs))
for _, j := range c.jobs {
jobs = append(jobs, j)
}
// Sort by created_at desc (simple bubble for small sets)
for i := 0; i < len(jobs); i++ {
for j := i + 1; j < len(jobs); j++ {
if jobs[j].CreatedAt.After(jobs[i].CreatedAt) {
jobs[i], jobs[j] = jobs[j], jobs[i]
}
}
}
if len(jobs) > limit {
jobs = jobs[:limit]
}
return jobs
}
func (c *CrucibleStore) trimLocked() {
if len(c.jobs) <= c.max {
return
}
oldest := ""
var oldestTime time.Time
for id, j := range c.jobs {
if oldest == "" || j.CreatedAt.Before(oldestTime) {
oldest = id
oldestTime = j.CreatedAt
}
}
if oldest != "" {
delete(c.jobs, oldest)
}
}