diff --git a/cmd/mesh-controller/push.go b/cmd/mesh-controller/push.go index ba1fd80..2713cd1 100644 --- a/cmd/mesh-controller/push.go +++ b/cmd/mesh-controller/push.go @@ -250,18 +250,6 @@ func pushCommand(ctx context.Context, args []string) error { return err } - // What every machine should be BEFORE this push composes anything. Composing a named node - // mints the provisions its consumers need, and a minted provision changes what the PROVIDER - // machine should be — its grant list is a pure read of secrets already issued (novox/hq - // issue 057). Captured now so that, after the send, "what changed because of this push" is - // a comparison rather than a guess. - var before map[string]string - if len(args) == 1 { - if before, err = wouldSend(ctx, open, nodes); err != nil { - return err - } - } - server, err := link.Connect(nil, nil) if err != nil { return err @@ -334,38 +322,47 @@ func pushCommand(ctx context.Context, args []string) error { } fmt.Printf("\n%d node(s) told\n", len(sending)) - // **A push leaves the mesh consistent, not just the machine it named** (novox/hq issue 057). - // The machines whose declaration changed because of THIS push — providers a provision was - // just minted from — are sent theirs too, by name, never silently: the alternative was a - // consumer that retries forever while nothing says the provider was left blind, and a rule - // ("push the provider node too") enforced by nothing. Bounded: a cascade send may itself - // mint, so this converges over a few rounds, each naming what changed and why. - if len(args) == 1 && before != nil { - delivered := map[string]bool{args[0]: true} - for round := 0; round < 3; round++ { - after, err := wouldSend(ctx, open, nodes) + // **A named push leaves the mesh consistent, not just the machine it named** (novox/hq + // issue 057, ADR 0083). Assigning a cross-node consumer mints a provision, and the PROVIDER's + // grant list is a pure read of secrets already issued — so after the named node is current, + // other machines can be behind *as a consequence*: their declaration now differs from what + // they were last sent. Those are flushed too, by name, in the push's own output. + // + // Compared against what each machine was last SENT, not against a before/after of this push: + // the mint usually happened at `assign` or `module issue`, before this command ran, so the + // only durable signal is "what it should be" versus "what it last received". A machine behind + // for an unrelated reason is caught here too, which is not a cost — a named push that knew a + // machine was behind and left it so would be the very silence this removes. Bounded: a + // flushed send may itself mint, so this converges over a few rounds. + if len(args) == 1 { + flushed := map[string]bool{args[0]: true} + for round := 0; round < 4; round++ { + would, err := wouldSend(ctx, open, nodes) + if err != nil { + return err + } + behind, err := inv.Waiting(ctx, would) if err != nil { return err } var also []string - for name, digest := range after { - if !delivered[name] && before[name] != "" && before[name] != digest { - also = append(also, name) + for _, m := range behind { + if !flushed[m.Node] { + also = append(also, m.Node) } } if len(also) == 0 { break } sort.Strings(also) - fmt.Printf("\nthis push changed what %s should be — a provision granted from "+ - "there; sending it too\n", strings.Join(also, ", ")) + fmt.Printf("\nthis push left %s behind — a provision granted from there, or a "+ + "declaration since changed; sending it too\n", strings.Join(also, ", ")) if err := sendTo(ctx, open, also); err != nil { return err } for _, name := range also { - delivered[name] = true + flushed[name] = true } - before = after } }