Merge pull request 'A node's have says which bus user list it wrote: delivery.Have's user-list field (ADR 0547 bridge, issue 1945)' (#83) from fix/1945-user-list-follows into main
This commit was merged in pull request #83.
This commit is contained in:
+34
-1
@@ -331,6 +331,27 @@ type Have struct {
|
|||||||
Phase Phase `json:"phase"`
|
Phase Phase `json:"phase"`
|
||||||
Health Health `json:"health"`
|
Health Health `json:"health"`
|
||||||
Detail string `json:"detail,omitempty"`
|
Detail string `json:"detail,omitempty"`
|
||||||
|
// UserList is what the node-engine of the node holding mesh-broker wrote of the bus's user list, in the have of the
|
||||||
|
// bus module (novox/hq ADR 0547 §7, §8). Nil on every other have.
|
||||||
|
UserList *UserListWritten `json:"user-list,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserListWritten is the bus's user list as the node-engine of the bus node wrote it to the bus module's file, applied
|
||||||
|
// alone and with no want (novox/hq ADR 0547 §8): where it was taken from, what that source holds, and what the file
|
||||||
|
// holds now. The bus reloads the file itself when its digest changes (the module's entrypoint), so Digest is the list
|
||||||
|
// the bus loads. Wanted differs from Digest only while a write failed, and Error says why.
|
||||||
|
type UserListWritten struct {
|
||||||
|
// Source is the key the list was taken from: the controller's composition of the bus module for this node
|
||||||
|
// (`<node>.<module>.<fp>`), or the grants part's `user-list` once it took over.
|
||||||
|
Source string `json:"source"`
|
||||||
|
Revision uint64 `json:"revision"`
|
||||||
|
// Wanted is the digest of the list the source holds, `sha256:<hex>`.
|
||||||
|
Wanted string `json:"wanted"`
|
||||||
|
// Digest is the digest of the file on the machine after the write, `sha256:<hex>`; empty when there is none.
|
||||||
|
Digest string `json:"digest,omitempty"`
|
||||||
|
Users int `json:"users"`
|
||||||
|
At time.Time `json:"at"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HaveNode is a node's own facts, written under HaveNodeKey. Each field is the node-engine's own JSON, carried whole:
|
// HaveNode is a node's own facts, written under HaveNodeKey. Each field is the node-engine's own JSON, carried whole:
|
||||||
@@ -470,7 +491,19 @@ func DecodePulse(data []byte) (Pulse, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SameHave says whether two haves are the same value, so a writer writes only when they differ.
|
// SameHave says whether two haves are the same value, so a writer writes only when they differ.
|
||||||
func SameHave(a, b Have) bool { return a == b }
|
func SameHave(a, b Have) bool {
|
||||||
|
ua, ub := a.UserList, b.UserList
|
||||||
|
a.UserList, b.UserList = nil, nil
|
||||||
|
if a != b || (ua == nil) != (ub == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ua == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
x, y := *ua, *ub
|
||||||
|
x.At, y.At = time.Time{}, time.Time{}
|
||||||
|
return x == y && ua.At.Equal(ub.At)
|
||||||
|
}
|
||||||
|
|
||||||
// SameHaveNode says whether two node facts are the same value, comparing each carried JSON without its spacing.
|
// SameHaveNode says whether two node facts are the same value, comparing each carried JSON without its spacing.
|
||||||
func SameHaveNode(a, b HaveNode) bool {
|
func SameHaveNode(a, b HaveNode) bool {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package delivery
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -149,6 +150,24 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
t.Fatalf("got %+v want %+v", out, in)
|
t.Fatalf("got %+v want %+v", out, in)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
t.Run("have with the user list", func(t *testing.T) {
|
||||||
|
at := time.Date(2026, 10, 25, 21, 40, 0, 0, time.UTC)
|
||||||
|
in := Have{FP: "f1", Phase: PhaseRunning, Health: HealthOK, UserList: &UserListWritten{Source: "novox.nats.f1",
|
||||||
|
Revision: 7, Wanted: "sha256:ab", Digest: "sha256:ab", Users: 93, At: at}}
|
||||||
|
b, err := EncodeHave(in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hasV1(t, b)
|
||||||
|
out, err := DecodeHave(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
in.V = Version
|
||||||
|
if !reflect.DeepEqual(out, in) || !out.UserList.At.Equal(at) {
|
||||||
|
t.Fatalf("got %+v want %+v", out, in)
|
||||||
|
}
|
||||||
|
})
|
||||||
t.Run("have node", func(t *testing.T) {
|
t.Run("have node", func(t *testing.T) {
|
||||||
in := HaveNode{Windows: json.RawMessage(`{"a":1}`), Busy: json.RawMessage(`true`)}
|
in := HaveNode{Windows: json.RawMessage(`{"a":1}`), Busy: json.RawMessage(`true`)}
|
||||||
b, err := EncodeHaveNode(in)
|
b, err := EncodeHaveNode(in)
|
||||||
@@ -275,6 +294,18 @@ func TestSame(t *testing.T) {
|
|||||||
if SameHave(a, b) {
|
if SameHave(a, b) {
|
||||||
t.Error("different haves same")
|
t.Error("different haves same")
|
||||||
}
|
}
|
||||||
|
// The user list is compared by what it says, never by where it is held (ADR 0547 §8).
|
||||||
|
at := time.Date(2026, 10, 25, 21, 0, 0, 0, time.UTC)
|
||||||
|
ua, ub := a, a
|
||||||
|
ua.UserList = &UserListWritten{Source: "k", Revision: 1, Wanted: "sha256:a", Digest: "sha256:a", Users: 2, At: at}
|
||||||
|
ub.UserList = &UserListWritten{Source: "k", Revision: 1, Wanted: "sha256:a", Digest: "sha256:a", Users: 2, At: at.In(time.Local)}
|
||||||
|
if !SameHave(ua, ub) || SameHave(ua, a) {
|
||||||
|
t.Error("equal user lists differ, or one said and one not are the same")
|
||||||
|
}
|
||||||
|
ub.UserList.Revision = 2
|
||||||
|
if SameHave(ua, ub) {
|
||||||
|
t.Error("different user lists same")
|
||||||
|
}
|
||||||
n := HaveNode{V: 1, Busy: json.RawMessage(`true`)}
|
n := HaveNode{V: 1, Busy: json.RawMessage(`true`)}
|
||||||
if !SameHaveNode(n, HaveNode{V: 1, Busy: json.RawMessage(`true`)}) || SameHaveNode(n, HaveNode{V: 1}) {
|
if !SameHaveNode(n, HaveNode{V: 1, Busy: json.RawMessage(`true`)}) || SameHaveNode(n, HaveNode{V: 1}) {
|
||||||
t.Error("have node")
|
t.Error("have node")
|
||||||
|
|||||||
Reference in New Issue
Block a user