128 lines
2.6 KiB
Go
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)
|
|
}
|
|
}
|