feat: models and indexes for the workload registry

This commit is contained in:
2026-08-07 08:45:00 +01:00
parent d1769fc886
commit 0838d1d735
3 changed files with 108 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
package models
import (
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)
// Workload kinds.
const (
WorkloadContainer = "container"
WorkloadUnit = "unit"
)
// Control actions.
const (
WorkloadStart = "start"
WorkloadStop = "stop"
WorkloadRestart = "restart"
)
// Workload is one container or one systemd unit.
type Workload struct {
Kind string `bson:"kind" json:"kind"` // container | unit
ID string `bson:"id" json:"id"` // container id, or unit name
Name string `bson:"name" json:"name"`
// State is deliberately NOT collapsed into a shared vocabulary across the
// two kinds. Containers report running/exited/paused/restarting/created;
// units report active/inactive/failed/activating. A failed unit and an
// exited container mean different things, and flattening them loses the
// distinction the operator needs.
State string `bson:"state" json:"state"`
Health string `bson:"health,omitempty" json:"health,omitempty"`
Image string `bson:"image,omitempty" json:"image,omitempty"`
Stack string `bson:"stack,omitempty" json:"stack,omitempty"` // compose project label
Ports []string `bson:"ports,omitempty" json:"ports,omitempty"`
Restarts int `bson:"restarts,omitempty" json:"restarts,omitempty"`
StartedAt time.Time `bson:"started_at,omitempty" json:"started_at,omitempty"`
// Protected is computed agent-side and reported so the UI can render the
// action disabled with a reason rather than offering a button whose refusal
// is already known. The field is the courtesy; the agent's own check is the
// boundary.
Protected bool `bson:"protected" json:"protected"`
}
// ServerWorkloads holds one server's whole workload list in ONE document.
type ServerWorkloads struct {
ID bson.ObjectID `bson:"_id,omitempty" json:"-"`
InstanceID string `bson:"instance_id" json:"-"`
ServerID string `bson:"server_id" json:"server_id"`
Hash string `bson:"hash" json:"hash"`
Workloads []Workload `bson:"workloads" json:"workloads"`
CollectedAt time.Time `bson:"collected_at" json:"collected_at"`
// A host with no Docker and a host with Docker running nothing both produce
// an empty list. One should read "not in use here", the other "nothing
// running", and only the second deserves any alarm.
//
// The error strings separate a third case the booleans cannot: installed
// with the daemon down. "Not installed" and "installed but not responding"
// are different problems with different fixes.
DockerOK bool `bson:"docker_ok" json:"docker_ok"`
DockerError string `bson:"docker_error,omitempty" json:"docker_error,omitempty"`
SystemdOK bool `bson:"systemd_ok" json:"systemd_ok"`
SystemdError string `bson:"systemd_error,omitempty" json:"systemd_error,omitempty"`
}
@@ -0,0 +1,34 @@
package services
import (
"context"
"log"
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/db"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
// EnsureWorkloadIndexes declares the indexes for the workload registry.
//
// It warns rather than being fatal, matching EnsureSecretIndexes and
// EnsureVulnIndexes: a missing index degrades these queries to a collection
// scan, which is no reason to refuse to serve the fleet.
func EnsureWorkloadIndexes() error {
ctx := context.Background()
idx := []mongo.IndexModel{
{
Keys: bson.D{{Key: "instance_id", Value: 1}, {Key: "server_id", Value: 1}},
Options: options.Index().SetUnique(true),
},
// Multikey, for the fleet-wide "which servers run image X" query, which
// is the reason the snapshot is stored rather than fetched and discarded.
{Keys: bson.D{{Key: "instance_id", Value: 1}, {Key: "workloads.image", Value: 1}}},
}
if _, err := db.Col("server_workloads").Indexes().CreateMany(ctx, idx); err != nil {
log.Printf("warning: server_workloads indexes: %v", err)
}
return nil
}