Documentation
¶
Index ¶
- Constants
- func Apply(agentID, accountID bson.ObjectID, ch ModChange, applyNow bool, ...) ([]string, error)
- func ApplyConfigOnly(agentID, accountID bson.ObjectID, modReference, config string, applyNow bool, ...) ([]string, error)
- func ApplyPendingNow(agentID, accountID bson.ObjectID, trigger v2.TaskTrigger) ([]string, error)
- func Backfill() error
- func CountDirectByAgent(agentIDs []bson.ObjectID) (map[bson.ObjectID]int32, error)
- func DeleteAbsent(agentID bson.ObjectID, keep []string) error
- func EnsureIndexes() error
- func Get(agentID bson.ObjectID, modReference string) (*v2.AgentModSchema, error)
- func Init() error
- func ListForAgent(agentID bson.ObjectID) ([]v2.AgentModSchema, error)
- func RefreshNeedsUpdate() error
- func ReportInstalled(agentID bson.ObjectID, mods []v2.InstalledMod) error
- func Resolve(agentID bson.ObjectID) (v2.Lockfile, error)
- func ResolveSelection(agentID bson.ObjectID, direct map[string]string) (v2.Lockfile, error)
- func SetConfig(agentID bson.ObjectID, modReference, config string) error
- func SetDesiredVersion(agentID bson.ObjectID, modReference, version string) error
- func UpsertMany(agentID, accountID bson.ObjectID, locks []v2.ModLock, ...) error
- type Change
- type ChangedMod
- type ModChange
Constants ¶
const ( ActionSyncMods = "syncmods" ActionStop = "stopsfserver" ActionStart = "startsfserver" )
const ( OpAdd = "add" OpRemove = "remove" OpSetVersion = "setVersion" OpUpdateAll = "updateAll" // OpApplyPending carries no modReference and no version: it escalates the // change that is ALREADY persisted and pending, it does not describe a new one. OpApplyPending = "applyPending" )
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
func Apply(agentID, accountID bson.ObjectID, ch ModChange, applyNow bool, trigger v2.TaskTrigger) ([]string, error)
Apply resolves the change, persists it, and enqueues the work.
applyNow is only consulted when the server is running: a stopped server needs no chain and no gate.
func ApplyConfigOnly ¶
func ApplyConfigOnly(agentID, accountID bson.ObjectID, modReference, config string, applyNow bool, trigger v2.TaskTrigger) ([]string, error)
ApplyConfigOnly persists an edit to a mod's .cfg text and enqueues a sync.
A config-text edit moves no versions, so Diff on the resulting lockfile is empty and Apply's diff-empty short circuit would silently drop it — but the text only reaches the agent's disk inside ModLock.Config during a sync, so skipping the enqueue here would leave the running server on the old config indefinitely. The diff check is therefore skipped deliberately, not an oversight: for a config-only change, "the lockfile didn't change" says nothing about whether the agent needs to re-sync.
func ApplyPendingNow ¶
ApplyPendingNow escalates an already-deferred change to run immediately.
The selection was persisted when the change was deferred, so Diff is empty and Apply's short circuit would silently drop this — but the pending sync is real and is only waiting for the server to stop. The diff check is skipped deliberately, exactly as in ApplyConfigOnly: "the lockfile didn't change" says nothing about whether a sync is still owed.
Nothing is persisted here either; there is nothing new to persist. planFor sees the pending sync id plus applyNow and RE-POINTS that same sync onto a fresh stop -> sync -> start chain rather than adding a second one.
func Backfill ¶
func Backfill() error
Backfill migrates the old embedded modConfig.selectedMods array into agentmods.
It is idempotent: an agent whose array has already been unset does not match the query below, so this is safe to run on every boot until it is deleted.
A mod that is permanently absent from the catalogue (pruned, or a lookup that keeps erroring) will block that agent's migration on EVERY boot, and will say so in the error log every time. That is deliberate: a loud, recoverable stall is preferable to a silent, permanent loss of the agent's mod selection, and only an operator can tell the two failure modes apart.
func CountDirectByAgent ¶
CountDirectByAgent counts each agent's directly-selected mods - the user's own selection, not the resolved dependency closure - in a single aggregation over the whole batch. The dashboard lists every agent on an account and needs a count per card; a count query per agent would be an N+1 on the most-loaded page in the product.
Callers must pass only agent IDs they have already authorised: this scopes to the IDs given and nothing else, so it neither widens nor enforces account access.
Agents with no mods are absent from the aggregation's output entirely, so the map simply has no entry for them and a lookup yields the zero value - which is the count you want.
func DeleteAbsent ¶
DeleteAbsent removes the agent's rows for mods no longer in the lockfile. It is how a removed mod, and a dependency nothing needs any more, leave the selection.
func EnsureIndexes ¶
func EnsureIndexes() error
EnsureIndexes creates the indexes the collection's correctness depends on. uniq_agent_mod is the upsert key and the guarantee that one agent cannot hold two rows for the same mod.
func Init ¶
func Init() error
Init prepares the collection at boot: indexes first, then the migration. Call this once during boot, next to agenttask.EnsureIndexes() and before agenttask.StartDispatcher() - the migration must finish before any task can be dispatched against a lockfile that assumes agentmods already holds the agent's selection.
func ListForAgent ¶
func ListForAgent(agentID bson.ObjectID) ([]v2.AgentModSchema, error)
func RefreshNeedsUpdate ¶
func RefreshNeedsUpdate() error
RefreshNeedsUpdate flags every agent mod whose catalogue version has moved past its pin. It never touches desiredVersion and never enqueues a task: a version bump is always a user action, so a bad mod release cannot take down a live server unattended.
This replaces CheckAgentModsConfigs, which loaded every agent document in the database and wrote every mod config back to compute this boolean. Here it is one updateMany per catalogue mod, keyed on modReference, never touching an agent document at all.
func ReportInstalled ¶
func ReportInstalled(agentID bson.ObjectID, mods []v2.InstalledMod) error
ReportInstalled records what the agent actually has on disk. Mods the agent reports but which are not in its selection are ignored: the next sync removes them from the disk, and creating rows for them here would resurrect them.
func ResolveSelection ¶
ResolveSelection pins a hypothetical direct selection: modReference -> the version constraint the user pinned, or "" for "latest compatible".
This is the one place the dependency graph is computed. The agent never resolves anything, so two agents cannot disagree, and an impossible selection is an error the user sees at click time rather than a task that dies later.
func SetDesiredVersion ¶
func UpsertMany ¶
func UpsertMany(agentID, accountID bson.ObjectID, locks []v2.ModLock, modIDs map[string]bson.ObjectID) error
UpsertMany writes a resolved lockfile back as the agent's selection: one upsert per mod, keyed on uniq_agent_mod. It sets desiredVersion, direct, and config, and leaves installed/installedVersion alone — those belong to the agent's report, and clobbering them here would make every sync look necessary.
Types ¶
type Change ¶
type Change struct {
Added []ChangedMod `json:"added"`
Removed []ChangedMod `json:"removed"`
Changed []ChangedMod `json:"changed"`
}
func Diff ¶
func Diff(current []v2.AgentModSchema, next v2.Lockfile) Change
Diff compares the agent's current selection against a freshly resolved lockfile. It is pure, and it is what both Preview (which renders it) and Apply (which acts on it) are built from.
type ChangedMod ¶
type ChangedMod struct {
ModReference string `json:"modReference"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
// Dependency is true when the resolver pulled this mod in rather than the user
// choosing it. "Also installs: A, B, C".
Dependency bool `json:"dependency"`
}
ChangedMod is one line of a change, as the confirmation dialog renders it.