Merge pull request 'mesh-grants writes its memberships bare through the mesh-sdk codec (issue 774)' (#114) from fix/memberships-shape into main
Reviewed-on: #114
This commit was merged in pull request #114.
This commit is contained in:
@@ -3,7 +3,7 @@ module mesh-grants
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030143623-83cf97b690f5
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0 h1:lKU3HjNXGiVbLW2hUgFjU/TMTUYMlNnnmZxG5T7biQA=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030001116-2da0de75c3f0/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030143623-83cf97b690f5 h1:hyyaB1rU7uzlQZvWAxZ9P6pL/4LCFpOVXMRgn9F7fic=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261030143623-83cf97b690f5/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -23,6 +25,7 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
"git.novox.be/novox/mesh-sdk/go/membership"
|
||||
"git.novox.be/novox/mesh-sdk/go/outward"
|
||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
||||
|
||||
@@ -1296,7 +1299,7 @@ func TestGrants(t *testing.T) {
|
||||
}
|
||||
|
||||
ms, _ := p.MembershipOf("a", "mod-x")
|
||||
if mo, _ := ms.(map[string]any); !strings.Contains(fmt.Sprint(mo["published"]), "last_pass") || mo["composed"] == nil {
|
||||
if mo, _ := ms.(map[string]any); !strings.Contains(fmt.Sprint(mo["published"]), "membership:") || mo["last_pass"] == nil || mo["composed"] == nil {
|
||||
t.Errorf("membership = %v", ms)
|
||||
}
|
||||
gr, _ := p.Grants("mod-x")
|
||||
@@ -1727,7 +1730,7 @@ func TestGrants(t *testing.T) {
|
||||
return m
|
||||
}, true, "", ""},
|
||||
{"a change that drops verified-sender its holder still asks for is not published, and named", verified, false, "channel telegram verified-sender of a/mod-x", telegramClaim},
|
||||
{"a change that drops verified-sender its holder no longer asks for is published", verified, true, `"published":"published"`, `[{"name":"channel","scope":"mesh","kind":"telegram"}]`},
|
||||
{"a change that drops verified-sender its holder no longer asks for is published", verified, true, "published:published", `[{"name":"channel","scope":"mesh","kind":"telegram"}]`},
|
||||
{"a change that takes the last holder from a seat a placed module claims is not published, the seat named", func(m map[string]any) map[string]any {
|
||||
m["seats"] = []any{map[string]any{"seat": brokerName, "verb": brokerVerb, "subject": "mesh.seat." + brokerName + ".tool." + brokerVerb}}
|
||||
return m
|
||||
@@ -1755,25 +1758,38 @@ func TestGrants(t *testing.T) {
|
||||
b.stream = map[string][]byte{"mesh.assignment." + key: raw}
|
||||
b.seqs = map[string]uint64{"mesh.assignment." + key: 4}
|
||||
}
|
||||
res := partOn(b).Compose()
|
||||
res := partOn(b).Pass()
|
||||
if got := slices.Contains(res.Published, key); got != row.published {
|
||||
t.Fatalf("published %v, not published %v", res.Published, res.NotPublished)
|
||||
}
|
||||
// The state holds the bare membership the assignments stream carries, through mesh-sdk's codec, and
|
||||
// only one published (issue 774); what became of it is the kept pass's, which grants_membership reads.
|
||||
state, _, inState, _ := b.ownGet(grants.StateMemberships, key)
|
||||
ms, _ := partOn(b).MembershipOf("a", "mod-x")
|
||||
pass := fmt.Sprint(ms.(map[string]any)["last_pass"])
|
||||
if row.published {
|
||||
if row.names == "" && row.last != nil && !strings.Contains(string(b.stream["mesh.assignment."+key]), "10.0.0.1") {
|
||||
t.Errorf("the private network of the last membership is not carried: %s", b.stream["mesh.assignment."+key])
|
||||
}
|
||||
if raw, _, _, _ := b.ownGet(grants.StateMemberships, key); !strings.Contains(string(raw), row.names) {
|
||||
t.Errorf("the pass does not name %q: %s", row.names, raw)
|
||||
got, err := membership.DecodeGrantsMembership(state)
|
||||
var sent membership.Membership
|
||||
_ = json.Unmarshal(b.stream["mesh.assignment."+key], &sent)
|
||||
if err != nil || !reflect.DeepEqual(got, sent) || bytes.Contains(state, []byte(`"membership"`)) {
|
||||
t.Errorf("the state is not the bare membership published (%v): %s", err, state)
|
||||
}
|
||||
if !strings.Contains(pass, row.names) {
|
||||
t.Errorf("the pass does not name %q: %s", row.names, pass)
|
||||
}
|
||||
return
|
||||
}
|
||||
if inState {
|
||||
t.Errorf("a membership held back reached the state: %s", state)
|
||||
}
|
||||
said := strings.Join(res.NotPublished, "\n")
|
||||
if !strings.Contains(said, grants.ShadowDifference) || !strings.Contains(said, row.names) {
|
||||
t.Errorf("the loss is not named: %s", said)
|
||||
}
|
||||
ms, _ := partOn(b).MembershipOf("a", "mod-x")
|
||||
if !strings.Contains(fmt.Sprint(ms), row.names) {
|
||||
if !strings.Contains(pass, row.names) {
|
||||
t.Errorf("grants_membership does not name what would be lost: %v", ms)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -106,8 +106,12 @@ type Result struct {
|
||||
MembershipsWritten int `json:"memberships_written"`
|
||||
Published []string `json:"published,omitempty"`
|
||||
NotPublished []string `json:"not_published,omitempty"`
|
||||
Assertions int `json:"assertions"`
|
||||
AssertionsWritten int `json:"assertions_written"`
|
||||
// Retired is, by `<node>.<module>`, the seat verbs its last published membership served that mesh-sdk's seat
|
||||
// catalogue no longer has (Membership.Retired): the pass keeps it, since the state memberships holds the bare
|
||||
// membership alone (issue 774).
|
||||
Retired map[string][]string `json:"retired,omitempty"`
|
||||
Assertions int `json:"assertions"`
|
||||
AssertionsWritten int `json:"assertions_written"`
|
||||
// Asserted is each module whose declared streams this pass created, `<module> at <digest>: created …`, and
|
||||
// NotAsserted each creation refused, `<module> <stream>: <why>` (issue 1790).
|
||||
Asserted []string `json:"asserted,omitempty"`
|
||||
@@ -320,9 +324,19 @@ func (p *Part) Compose() Result {
|
||||
case m.Published != MembershipUnchanged:
|
||||
res.NotPublished = append(res.NotPublished, key+": "+m.Published)
|
||||
}
|
||||
data, _ := json.Marshal(m)
|
||||
if p.changed(&res, StateMemberships, key, data) {
|
||||
res.MembershipsWritten++
|
||||
if len(m.Retired) > 0 {
|
||||
if res.Retired == nil {
|
||||
res.Retired = map[string][]string{}
|
||||
}
|
||||
res.Retired[key] = m.Retired
|
||||
}
|
||||
// The state holds the bare membership as the assignments stream carries it, through mesh-sdk's one codec (issue
|
||||
// 774): only one published now or unchanged, so a membership held back never reaches a reader by this way. What
|
||||
// became of each at this pass is the pass's own (Published, NotPublished, Retired), kept with it (Pass).
|
||||
if now || m.Published == MembershipUnchanged {
|
||||
if p.changed(&res, StateMemberships, key, membership.EncodeGrantsMembership(m.Membership)) {
|
||||
res.MembershipsWritten++
|
||||
}
|
||||
}
|
||||
}
|
||||
held, heldErr := HeldStreams(p.Call)
|
||||
|
||||
@@ -4,9 +4,11 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/membership"
|
||||
)
|
||||
|
||||
// The part's tools, as its manifest lists them. Module tools in this milestone: a `mesh-*` seat must be in mesh-sdk's own
|
||||
@@ -133,16 +135,52 @@ func (p *Part) MembershipOf(node, module string) (any, error) {
|
||||
}
|
||||
out["composed"] = why
|
||||
}
|
||||
if raw, rev, found, err := p.Store.Get(StateMemberships, node+"."+module); err != nil {
|
||||
// The state holds the bare membership last published (mesh-sdk's codec, issue 774); what became of it at the last
|
||||
// pass is the pass's, kept in user-list at KeyPass.
|
||||
key := node + "." + module
|
||||
if raw, rev, found, err := p.Store.Get(StateMemberships, key); err != nil {
|
||||
out["published"] = "not readable: " + err.Error()
|
||||
} else if found {
|
||||
out["published"] = map[string]any{"revision": rev, "last_pass": json.RawMessage(raw)}
|
||||
} else if !found {
|
||||
out["published"] = "no pass of the part's process has published it yet"
|
||||
} else if was, err := membership.DecodeGrantsMembership(raw); err != nil {
|
||||
out["published"] = fmt.Sprintf("revision %d is not readable: %v", rev, err)
|
||||
} else {
|
||||
out["published"] = "no pass of the part's process has composed it yet"
|
||||
out["published"] = map[string]any{"revision": rev, "digest": digestOf(was), "membership": was}
|
||||
}
|
||||
out["last_pass"] = p.passOf(key)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// passOf is what the last pass the part's process kept says of one membership: published, unchanged or why not, and
|
||||
// the seat verbs it dropped as retired.
|
||||
func (p *Part) passOf(key string) any {
|
||||
raw, rev, found, err := p.Store.Get(StateUserList, KeyPass)
|
||||
if err != nil {
|
||||
return "not readable: " + err.Error()
|
||||
}
|
||||
if !found {
|
||||
return "no pass of the part's process has been kept yet"
|
||||
}
|
||||
var res Result
|
||||
if err := json.Unmarshal(raw, &res); err != nil {
|
||||
return "not readable: " + err.Error()
|
||||
}
|
||||
said := MembershipUnchanged
|
||||
if slices.Contains(res.Published, key) {
|
||||
said = MembershipPublished
|
||||
}
|
||||
for _, n := range res.NotPublished {
|
||||
if k, why, _ := strings.Cut(n, ": "); k == key {
|
||||
said = why
|
||||
}
|
||||
}
|
||||
out := map[string]any{"revision": rev, "published": said}
|
||||
if r := res.Retired[key]; len(r) > 0 {
|
||||
out["retired"] = r
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Held answers every membership the part's process held back at its last pass, each `<node>.<module>` with why (what
|
||||
// publishing would lose), from the pass it keeps (issue 1945).
|
||||
func (p *Part) Held() (any, error) {
|
||||
|
||||
Reference in New Issue
Block a user