An operator ran `mesh-host reconcile` on an adopted control-node with twelve modules assigned. It applied the bundle the host carries — the genesis declaration, foundation only, converged: recreated the store, failed on the broker's held port, wrote the converged base filter and started its service, and stopped at the first failing action. The filter closed the machine for forty-five minutes. The host reported the node adopted in every report, the declaration said converged, and nothing compared the two; nothing was printed before acting (hq issue 104). The host now records the node's mode — from every declaration the mesh sends, and at genesis from what the operator said — and refuses, at the point of application, a declaration that says the other mode, naming both and the act that changes it. Only a declaration the link delivers, signed, changes the mode: that is how `converge` and `adopt` arrive, so the flip still works and nothing else can do it. Genesis marks the bundle consumed, with the digest of what it applied, so `reconcile` holds a node the mesh has spoken to against what the mesh last said and never the bundle, and refuses the carried bytes when they are not what genesis applied. A file is refused when it is not what the mesh last said: a declaration carries no sequence and no issued-at, so the host cannot tell older from newer, and says so. Both commands print what they would change — a hold, a removal, an action named as one — before touching anything, and --dry-run is that list and nothing more.
33 lines
1.4 KiB
Go
33 lines
1.4 KiB
Go
package apply
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// Defends novox/hq issue 104: a declaration is refused for the other mode than the node is in,
|
|
// naming both and the act that changes it; a node no mode has been said to takes either.
|
|
func TestADeclarationForTheOtherModeIsRefused(t *testing.T) {
|
|
converged := parse(t, `{"declaration":1,"resources":[{"id":"a","type":"file","path":"/tmp/a","content":"x\n"}]}`)
|
|
adopted := parse(t, `{"declaration":1,"adoption":{"taken":[]},"resources":[{"id":"a","type":"file","path":"/tmp/a","content":"x\n"}]}`)
|
|
|
|
err := CheckMode(store.State{Mode: store.ModeAdopted}, converged)
|
|
if err == nil || !strings.Contains(err.Error(), "this node is adopted; the declaration says converged") ||
|
|
!strings.Contains(err.Error(), "`converge`") {
|
|
t.Errorf("a converged declaration on an adopted node: %v", err)
|
|
}
|
|
err = CheckMode(store.State{Mode: store.ModeConverged}, adopted)
|
|
if err == nil || !strings.Contains(err.Error(), "this node is converged; the declaration says adopted") ||
|
|
!strings.Contains(err.Error(), "`adopt`") {
|
|
t.Errorf("an adopted declaration on a converged node: %v", err)
|
|
}
|
|
if err := CheckMode(store.State{Mode: store.ModeAdopted}, adopted); err != nil {
|
|
t.Errorf("the node's own mode was refused: %v", err)
|
|
}
|
|
if err := CheckMode(store.State{}, converged); err != nil {
|
|
t.Errorf("a node in no mode yet refused a declaration: %v", err)
|
|
}
|
|
}
|