mesh/delivery merged: source.main moved to novox/mesh-host@58b4f8b6bc07
mesh/check being carried to bf667a602d21 on 58b4f8b6bc07 | from 9a8b3870bad0
A reboot left the have running, so a unit that failed to start at boot was never started again and nothing said so. Baseline now asks the machine; the judge's look calls Agent.Reconcile once per down edge, leaving held and unwanted modules alone.
89 lines
2.7 KiB
Go
89 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"github.com/novox/mesh-host/internal/apply"
|
|
"github.com/novox/mesh-host/internal/liveness"
|
|
)
|
|
|
|
// heldBy is the containers an open maintenance window holds still, by runtime name (ADR 0189), at now.
|
|
func heldBy(windows *apply.Windows) func(time.Time) map[string]bool {
|
|
return func(now time.Time) map[string]bool {
|
|
held := map[string]bool{}
|
|
for _, w := range windows.Open(now) {
|
|
for _, name := range w.Holds {
|
|
held[name] = true
|
|
}
|
|
}
|
|
return held
|
|
}
|
|
}
|
|
|
|
// inactiveIn is what the agent asks at its start of a module it believes runs (ADR 0568 §1): whether the machine shows a
|
|
// long-running resource the composition declares running not active, read from the service manager and the container
|
|
// runtime and from no record. A resource held by an open maintenance window, or waiting for a library, is not counted;
|
|
// one that cannot be read is not counted either (unknown is not inactive), and neither is one the manager is restarting.
|
|
func inactiveIn(rt liveness.Runtime, held func(time.Time) map[string]bool) func(context.Context, delivery.Composed) bool {
|
|
return func(ctx context.Context, c delivery.Composed) bool {
|
|
d, err := declarationOf(c)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
var holding map[string]bool
|
|
if held != nil {
|
|
holding = held(time.Now())
|
|
}
|
|
return anyInactive(ctx, rt, liveness.LongRunning(d, withWaiting(nil, apply.WaitingOnLibrary(d))), holding)
|
|
}
|
|
}
|
|
|
|
// anyInactive reads each group of resources once (the containers, each service manager's units) and says whether any is
|
|
// not active.
|
|
func anyInactive(ctx context.Context, rt liveness.Runtime, resources []liveness.Resource, held map[string]bool) bool {
|
|
type group struct {
|
|
kind, scope, user string
|
|
targets []string
|
|
}
|
|
var groups []*group
|
|
at := map[string]*group{}
|
|
for _, r := range resources {
|
|
if r.Kind == liveness.KindContainer && held[r.Target] {
|
|
continue
|
|
}
|
|
g := &group{kind: r.Kind, scope: r.Scope, user: r.User}
|
|
if r.Kind == liveness.KindContainer {
|
|
g.scope, g.user = "", ""
|
|
}
|
|
key := g.kind + "/" + g.scope + "/" + g.user
|
|
if r.Kind != liveness.KindContainer {
|
|
key = "units/" + g.scope + "/" + g.user
|
|
}
|
|
if at[key] == nil {
|
|
at[key] = g
|
|
groups = append(groups, g)
|
|
}
|
|
at[key].targets = append(at[key].targets, r.Target)
|
|
}
|
|
for _, g := range groups {
|
|
var got map[string]liveness.Observed
|
|
var err error
|
|
if g.kind == liveness.KindContainer {
|
|
got, err = rt.Containers(ctx, g.targets)
|
|
} else {
|
|
got, err = rt.Units(ctx, g.scope, g.user, g.targets)
|
|
}
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, t := range g.targets {
|
|
if o := got[t]; !o.Found || !(o.Running || o.Restarting) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|