diff --git a/cmd/mesh-controller/grants_step_store_test.go b/cmd/mesh-controller/grants_step_store_test.go index 7f675c07..3248a1b4 100644 --- a/cmd/mesh-controller/grants_step_store_test.go +++ b/cmd/mesh-controller/grants_step_store_test.go @@ -105,7 +105,10 @@ func TestTheGrantsStepSendsTheUserListAndNothingElse(t *testing.T) { t.Fatal(err) } } - kept := keepingEveryBuild(keepingRecorded(ctx)) + // Kept for anchor alone. laptop is never recorded as sent, on purpose: composing anchor resolves laptop + // too (who is on the private network), and a step that kept every machine's builds refused anchor's + // composition for want of laptop's last send (repo-check of #230 at 0f853e93). + kept := keepingEveryBuild(keepingRecorded(ctx), "anchor") // What anchor was last sent: everything at the builds of before the merge, as an ordinary send sends it. _, before := compose(keepingRecorded(ctx)) diff --git a/cmd/mesh-controller/grants_step_test.go b/cmd/mesh-controller/grants_step_test.go index 2820bb51..48b34bb5 100644 --- a/cmd/mesh-controller/grants_step_test.go +++ b/cmd/mesh-controller/grants_step_test.go @@ -43,7 +43,7 @@ func sendsRecorded(t *testing.T, holder string, behind bool, refuse error) *[]aS return holder, behind, nil } sendRollout = func(ctx context.Context, _ *stores, names []string) ([]string, error) { - s := aSend{names: append([]string(nil), names...), keepsAll: everyBuildKept(ctx)} + s := aSend{names: append([]string(nil), names...), keepsAll: len(names) == 1 && everyBuildKept(ctx, names[0])} for n := range scopeOf(ctx).judged { s.judged = append(s.judged, n) } @@ -168,6 +168,15 @@ func TestOnlyTheUserListsContentIsExempt(t *testing.T) { } } +// The step keeps the builds of the machine it sends alone: the others its composition resolves are composed +// as any send composes them. +func TestTheGrantsStepKeepsOneMachinesBuilds(t *testing.T) { + ctx := keepingEveryBuild(t.Context(), "novox") + if !everyBuildKept(ctx, "novox") || everyBuildKept(ctx, "ace") || everyBuildKept(t.Context(), "novox") { + t.Error("the grants step keeps the builds of a machine it does not send") + } +} + // The delivery plan says the step before the merge. func TestAChangePlanSaysTheGrantsStep(t *testing.T) { const catalogue_ = "http://forge.internal:20000/novox/mesh-catalog.git" diff --git a/cmd/mesh-controller/push.go b/cmd/mesh-controller/push.go index 30e3fd84..2e134b15 100644 --- a/cmd/mesh-controller/push.go +++ b/cmd/mesh-controller/push.go @@ -1238,7 +1238,7 @@ func sendToEach(ctx context.Context, open *stores, names []string) ([]string, er numbered.stamp(&declared) // **The grants step sends the user list and nothing else** (novox/hq issue 490): judged on this very // declaration, the one sent, so nothing composed between a check and the send can slip through. - if everyBuildKept(ctx) { + if everyBuildKept(ctx, name) { other, err := grantsOnlyIn(ctx, open, name, plan, declared) if err != nil { return nil, err diff --git a/cmd/mesh-controller/recorded_kept.go b/cmd/mesh-controller/recorded_kept.go index 5c81bec8..a8e8cf5b 100644 --- a/cmd/mesh-controller/recorded_kept.go +++ b/cmd/mesh-controller/recorded_kept.go @@ -63,19 +63,24 @@ func keptExcept(ctx context.Context) (map[string]bool, bool) { type keepEveryBuildKey struct{} -// keepingEveryBuild is a context whose sends compose every module — recorded or rolling out — at the build -// its machine was last sent (novox/hq issue 490): the grants step, which sends the machine holding the bus -// its new user list and nothing of any module's new code. That code waits there for a gate like any other -// move; the list must not, or the first machine's gate is judged against a bus that refuses what the change -// newly grants. -func keepingEveryBuild(ctx context.Context) context.Context { - return context.WithValue(ctx, keepEveryBuildKey{}, true) +// keepingEveryBuild is a context whose sends compose every module of one machine — recorded or rolling out — +// at the build that machine was last sent (novox/hq issue 490): the grants step, which sends the machine +// holding the bus its new user list and nothing of any module's new code. That code waits there for a gate +// like any other move; the list must not, or the first machine's gate is judged against a bus that refuses +// what the change newly grants. +// +// **That machine alone.** Composing one machine resolves the others too — who is on the private network, +// who answers a requirement — on the same context, and those are no part of the step's send: they are +// composed as any send composes them. Kept for every machine, a machine whose last send is not known +// refused the bus's machine's composition. +func keepingEveryBuild(ctx context.Context, node string) context.Context { + return context.WithValue(ctx, keepEveryBuildKey{}, node) } -// everyBuildKept is whether this context keeps every module at the build its machine runs. -func everyBuildKept(ctx context.Context) bool { - on, _ := ctx.Value(keepEveryBuildKey{}).(bool) - return on +// everyBuildKept is whether this context keeps every module of this machine at the build it runs. +func everyBuildKept(ctx context.Context, node string) bool { + on, _ := ctx.Value(keepEveryBuildKey{}).(string) + return on != "" && on == node } // recordedKept is, for a send under keepingRecorded, every recorded module the machine was last sent a @@ -87,7 +92,7 @@ func everyBuildKept(ctx context.Context) bool { // 490), and a machine whose last send is not known refuses the send: there is nothing to keep it at, and // composing the mesh's builds would be the very move the grants step must not make. func recordedKept(ctx context.Context, open *stores, node string) (map[string]string, error) { - every := everyBuildKept(ctx) + every := everyBuildKept(ctx, node) skip, on := keptExcept(ctx) if !on && !every { return nil, nil @@ -150,7 +155,7 @@ func keepRecorded(ctx context.Context, open *stores, node string, shelf map[stri if err != nil { return nil, err } - if !found && everyBuildKept(ctx) { + if !found && everyBuildKept(ctx, node) { return nil, fmt.Errorf("%s runs %s's build %s, which the build records no longer hold: the bus's user "+ "list cannot be sent there alone with it kept (novox/hq issue 490)", node, m, short(kept[m])) } diff --git a/cmd/mesh-controller/release.go b/cmd/mesh-controller/release.go index 8be20f46..555ebeb2 100644 --- a/cmd/mesh-controller/release.go +++ b/cmd/mesh-controller/release.go @@ -257,7 +257,7 @@ func ungatedIn(ctx context.Context, open *stores, names []string, addedHolder st // The grants step composes every module at the build its machine was last sent (novox/hq issue 490): // a move it keeps is not made. Read, not assumed, so a move it could not keep is still refused here. var keeps map[string]string - if everyBuildKept(ctx) { + if everyBuildKept(ctx, n) { if keeps, err = recordedKept(ctx, open, n); err != nil { return nil, err } @@ -968,7 +968,7 @@ func grantsStep(ctx context.Context, open *stores, node string) *inventory.Grant // list is the one already sent. return nil } - if _, err := sendRollout(keepingEveryBuild(withScope(ctx, sendScope{})), open, []string{holder}); err != nil { + if _, err := sendRollout(keepingEveryBuild(withScope(ctx, sendScope{}), holder), open, []string{holder}); err != nil { fmt.Printf("grants step: the bus's user list could not be sent to %s ahead of %s, which is sent without "+ "it — the bus may refuse what the send newly grants: %v\n", holder, node, err) return &inventory.GrantsStep{Node: holder, Failed: err.Error()}