diff --git a/cmd/mesh-controller/push.go b/cmd/mesh-controller/push.go index 2319213..74ec025 100644 --- a/cmd/mesh-controller/push.go +++ b/cmd/mesh-controller/push.go @@ -502,8 +502,14 @@ func composeEach(names []string, continue } if len(declared.Resources) == 0 { - fmt.Printf("%s is assigned nothing — skipped\n", name) - continue + // Sent, not skipped (novox/hq issue 127). A node whose declaration composes to + // nothing may have HELD something before — the broker opening a placement gave it, + // say — and skipping the empty declaration leaves that last resource in force + // forever, re-applied by the node's own heartbeat, with no way for the mesh to say + // it is gone. An empty declaration is the correction: the host drops what the mesh + // owned and keeps what it found (the adoption envelope still rides along). A node + // that never held anything applies it as the no-op it is. + fmt.Printf("%s owns nothing now — sent so it drops what it last held\n", name) } sending = append(sending, readyNode{name, declared}) } diff --git a/cmd/mesh-controller/push_test.go b/cmd/mesh-controller/push_test.go index 3e6dc9d..aeaabfa 100644 --- a/cmd/mesh-controller/push_test.go +++ b/cmd/mesh-controller/push_test.go @@ -39,12 +39,14 @@ func TestOneUnresolvableNodeStillLetsTheRestBeSent(t *testing.T) { } } -// And a machine assigned nothing is neither sent nor a refusal — it is nothing to say. -func TestAMachineAssignedNothingIsNotARefusal(t *testing.T) { +// A machine whose declaration composes to nothing is SENT the empty declaration, not skipped +// (novox/hq issue 127): it may have held something before, and only sending the empty +// declaration tells it to drop what the mesh owned. It is never a refusal. +func TestAnEmptyDeclarationIsSentSoTheNodeDropsWhatItHeld(t *testing.T) { sending, refusals := composeEach([]string{"spare"}, func(string) (sendable, error) { return sendable{}, nil }) - if len(sending) != 0 || len(refusals) != 0 { - t.Errorf("a machine assigned nothing was treated as something: %v / %v", sending, refusals) + if len(sending) != 1 || len(refusals) != 0 { + t.Errorf("an empty declaration must be sent, not skipped or refused: %v / %v", sending, refusals) } }