Read what a node runs inside its own composition from its haves, not from what the agent applied, so a restarted agent's first composition keeps the bus (issue 1126, review of the fold round 1)
A fresh agent composes before it has applied anything (Running empty), so InOwn of Running folded only mesh-wireguard and the switch would drop nats. The composer now watches the node's haves: a placed module whose have names no new-form fingerprint is folded; one with a new-form have whose want is removed is not folded. The shadow uses the same set, over the candidate's placement.
This commit is contained in:
@@ -71,10 +71,13 @@ type Composer struct {
|
||||
wants map[string]delivery.Want // by module, the node's own under agent.NodeModule
|
||||
placements map[string]delivery.Placement
|
||||
records map[string]delivery.NodeRecord
|
||||
modules map[string]delivery.Module
|
||||
values map[string]map[string]any // by module, this node's
|
||||
manifests map[string][]byte // by digest, read once
|
||||
handed map[string]string // composed key to the digest handed
|
||||
// inOwn is, by module, whether this node's have of it names no fingerprint of the new form (mesh-wireguard, a module
|
||||
// at a build of the old form): the node runs it inside its own composition, and composes it there again (issue 1126).
|
||||
inOwn map[string]bool
|
||||
modules map[string]delivery.Module
|
||||
values map[string]map[string]any // by module, this node's
|
||||
manifests map[string][]byte // by digest, read once
|
||||
handed map[string]string // composed key to the digest handed
|
||||
// sealedOwn is each own secret's sealed value as first handed, by module/name and generation: a value sealed again
|
||||
// is other bytes, so the one first had is kept while the generation stands, and the composition stays the same.
|
||||
sealedOwn map[string]ownSealed
|
||||
@@ -90,7 +93,7 @@ func New(env Env) *Composer {
|
||||
env.Say = func(string) {}
|
||||
}
|
||||
return &Composer{env: env, wants: map[string]delivery.Want{}, placements: map[string]delivery.Placement{},
|
||||
records: map[string]delivery.NodeRecord{}, modules: map[string]delivery.Module{}, values: map[string]map[string]any{},
|
||||
records: map[string]delivery.NodeRecord{}, inOwn: map[string]bool{}, modules: map[string]delivery.Module{}, values: map[string]map[string]any{},
|
||||
manifests: map[string][]byte{}, handed: map[string]string{}, sealedOwn: map[string]ownSealed{}}
|
||||
}
|
||||
|
||||
@@ -105,16 +108,19 @@ type ownSealed struct {
|
||||
type input struct {
|
||||
bucket, pattern string
|
||||
take func(c *Composer, u agent.Update)
|
||||
// changed, when set, takes the update in take's place and says whether it changed anything composed.
|
||||
changed func(c *Composer, u agent.Update) bool
|
||||
}
|
||||
|
||||
func (c *Composer) inputs() []input {
|
||||
node := c.env.Node
|
||||
return []input{
|
||||
{delivery.AssignerWantsBucket, "want." + node + ".>", (*Composer).takeWant},
|
||||
{delivery.PlacementBucket, ">", (*Composer).takePlacement},
|
||||
{delivery.HaveBucket, "*." + delivery.HaveNodeWord, (*Composer).takeRecord},
|
||||
{delivery.ModulesBucket, ">", (*Composer).takeModule},
|
||||
{ResolvedBucket, node + ".>", (*Composer).takeValue},
|
||||
{delivery.AssignerWantsBucket, "want." + node + ".>", (*Composer).takeWant, nil},
|
||||
{delivery.PlacementBucket, ">", (*Composer).takePlacement, nil},
|
||||
// Every node's record, and this node's haves: what it runs inside its own composition.
|
||||
{delivery.HaveBucket, ">", nil, (*Composer).takeHave},
|
||||
{delivery.ModulesBucket, ">", (*Composer).takeModule, nil},
|
||||
{ResolvedBucket, node + ".>", (*Composer).takeValue, nil},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +199,8 @@ func (c *Composer) Watch(ctx context.Context, _ string) (<-chan agent.Update, er
|
||||
dirty = true // a secret changed: its new generation is asked of the vault at the compose
|
||||
case t.u.Caught:
|
||||
caught[t.in] = true
|
||||
case ins[t.in].changed != nil:
|
||||
dirty = ins[t.in].changed(c, t.u) || dirty
|
||||
default:
|
||||
ins[t.in].take(c, t.u)
|
||||
dirty = true
|
||||
@@ -302,6 +310,56 @@ func (c *Composer) takeRecord(u agent.Update) {
|
||||
c.records[node] = *h.Record
|
||||
}
|
||||
|
||||
// takeHave takes a have key: a node's record (always composed again), or one of this node's modules' haves, which
|
||||
// changes what is composed only when whether it runs inside the node's own composition changes.
|
||||
func (c *Composer) takeHave(u agent.Update) bool {
|
||||
if isNodeKey(u.Key) {
|
||||
c.takeRecord(u)
|
||||
return true
|
||||
}
|
||||
return c.takeModuleHave(u)
|
||||
}
|
||||
|
||||
// takeModuleHave takes one of this node's modules' haves, and says whether it changed which it runs inside its own
|
||||
// composition: one whose have names no fingerprint of the new form and is not removed (nats on novox: a build-...
|
||||
// id; mesh-wireguard: none). Read from the haves, which outlast a restart, never from what the agent has applied
|
||||
// this run: a fresh agent composes before it applies anything (review of the fold, round 1).
|
||||
func (c *Composer) takeModuleHave(u agent.Update) bool {
|
||||
node, module, isNode, err := delivery.ParseHaveKey(u.Key)
|
||||
if err != nil || isNode || node != c.env.Node {
|
||||
return false
|
||||
}
|
||||
in := false
|
||||
if !u.Deleted {
|
||||
if h, err := delivery.DecodeHave(u.Value); err == nil {
|
||||
in = h.Phase != delivery.PhaseRemoved && !strings.HasPrefix(h.FP, delivery.SourcePrefix2)
|
||||
}
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.inOwn[module] == in {
|
||||
return false
|
||||
}
|
||||
if in {
|
||||
c.inOwn[module] = true
|
||||
} else {
|
||||
delete(c.inOwn, module)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// inOwnOf is, of the modules p places, those the node runs inside its own composition (c.inOwn), sorted; c.mu held.
|
||||
func (c *Composer) inOwnOf(p delivery.Placement) []string {
|
||||
var out []string
|
||||
for _, m := range p.Modules {
|
||||
if c.inOwn[m.Module] {
|
||||
out = append(out, m.Module)
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func (c *Composer) takeModule(u agent.Update) {
|
||||
name, err := delivery.ParseModulesKey(u.Key)
|
||||
if err != nil {
|
||||
@@ -359,15 +417,11 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
|
||||
}
|
||||
}
|
||||
_, nodeWanted := c.wants[agent.NodeModule]
|
||||
placement := c.placements[c.env.Node]
|
||||
// What it runs inside its own composition and no want names at a build of the new form stays there, as the
|
||||
// controller's composed writer folds it (issue 1126); compose folds none a want names.
|
||||
inOwn := c.inOwnOf(c.placements[c.env.Node])
|
||||
r := reader{c: c}
|
||||
c.mu.Unlock()
|
||||
// What it runs inside its own composition and no want names (mesh-wireguard, a module at a build of the old form)
|
||||
// stays there, as the controller's composed writer folds it (issue 1126).
|
||||
var inOwn []string
|
||||
if c.env.Running != nil {
|
||||
inOwn = frombus.InOwn(c.env.Running(), placement)
|
||||
}
|
||||
sort.Strings(behind)
|
||||
for _, module := range behind {
|
||||
digest, err := c.builtManifest(ctx, module, fps[module])
|
||||
|
||||
Reference in New Issue
Block a user