Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ca073938 | ||
|
|
b244a768a3 | ||
|
|
81d52719f4 | ||
|
|
f6a93fe74c |
@@ -28,24 +28,14 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
|||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
|
||||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
|
||||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
|
||||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
|
||||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
|
||||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
||||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
|
||||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|
||||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
|
||||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
|
||||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
+11
-11
@@ -184,17 +184,17 @@ func claimProblems(m Manifest) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range m.Claims {
|
for _, c := range m.Claims {
|
||||||
if seat, known := SeatNamed(c.Name); known {
|
if _, known := SeatNamed(c.Name); known {
|
||||||
if c.At() != seat.Scope {
|
// **A seat's scope and what it delivers are not judged here** (novox/hq ADR 0122).
|
||||||
problems = append(problems, fmt.Sprintf(
|
// This function runs wherever a manifest is parsed, and one of those places is the
|
||||||
"%s claims %s at scope %q, and %s is a %s seat",
|
// build machine, which has no store: there, `SeatNamed` answers from the set the
|
||||||
m.Module, c.Name, c.At(), c.Name, seat.Scope))
|
// binary shipped with, so a build would be refused for disagreeing with a compiled
|
||||||
}
|
// copy of data the control plane owns. Exactly that happened — a holder of the bus
|
||||||
if seat.Delivers != "" && !providesAt(m, seat.Delivers, seat.Scope) {
|
// seat was refused for not providing what a stale compiled row said the seat
|
||||||
problems = append(problems, fmt.Sprintf(
|
// delivered, while the store's own row said otherwise.
|
||||||
"%s claims %s, whose holder answers for %q, and %s does not provide %q at %s scope",
|
//
|
||||||
m.Module, c.Name, seat.Delivers, m.Module, seat.Delivers, seat.Scope))
|
// Both checks moved to CatalogueProblems, which only ever runs in the control plane,
|
||||||
}
|
// after UseSeats has replaced the set with the store's.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isSystemSeatName(c.Name) {
|
if isSystemSeatName(c.Name) {
|
||||||
|
|||||||
@@ -190,7 +190,22 @@ func CatalogueProblems(shelf Shelf) []string {
|
|||||||
}
|
}
|
||||||
s, isModuleSeat := declared[c.Name]
|
s, isModuleSeat := declared[c.Name]
|
||||||
if !isModuleSeat {
|
if !isModuleSeat {
|
||||||
continue // a mesh seat: already judged by claimProblems
|
// **A mesh seat is judged here and nowhere else** (novox/hq ADR 0122): the set is
|
||||||
|
// the store's, and this is the only place that runs with the store's set loaded.
|
||||||
|
// The parser cannot do it — it also runs on the build machine, against whatever
|
||||||
|
// set that binary was compiled with.
|
||||||
|
seat, _ := SeatNamed(c.Name)
|
||||||
|
if c.At() != seat.Scope {
|
||||||
|
problems = append(problems, fmt.Sprintf(
|
||||||
|
"%s claims %s at scope %q, and %s is a %s seat",
|
||||||
|
module, c.Name, c.At(), c.Name, seat.Scope))
|
||||||
|
}
|
||||||
|
if seat.Delivers != "" && !providesAt(m, seat.Delivers, seat.Scope) {
|
||||||
|
problems = append(problems, fmt.Sprintf(
|
||||||
|
"%s claims %s, whose holder answers for %q, and %s does not provide %q at %s scope",
|
||||||
|
module, c.Name, seat.Delivers, module, seat.Delivers, seat.Scope))
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if c.At() != s.At() {
|
if c.At() != s.At() {
|
||||||
problems = append(problems, fmt.Sprintf(
|
problems = append(problems, fmt.Sprintf(
|
||||||
|
|||||||
@@ -91,7 +91,10 @@ func TestAClaimMustMatchTheDeclaredScope(t *testing.T) {
|
|||||||
|
|
||||||
// The mesh's own seats still work, and are not shadowed by the derived half.
|
// The mesh's own seats still work, and are not shadowed by the derived half.
|
||||||
func TestTheMeshsOwnSeatsAreStillClaimable(t *testing.T) {
|
func TestTheMeshsOwnSeatsAreStillClaimable(t *testing.T) {
|
||||||
m := Manifest{Module: "nats", Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}}
|
// It delivers the bus, so its holder provides the bus — the rule this check now enforces.
|
||||||
|
m := Manifest{Module: "nats",
|
||||||
|
Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh}},
|
||||||
|
Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}}
|
||||||
if got := problemsFor(t, Shelf{"nats": m}); got != "" {
|
if got := problemsFor(t, Shelf{"nats": m}); got != "" {
|
||||||
t.Fatalf("a mesh seat was refused by the derived check: %s", got)
|
t.Fatalf("a mesh seat was refused by the derived check: %s", got)
|
||||||
}
|
}
|
||||||
@@ -106,3 +109,38 @@ func TestTheProblemsAreStable(t *testing.T) {
|
|||||||
t.Fatalf("unstable:\n%s\n%s", first, second)
|
t.Fatalf("unstable:\n%s\n%s", first, second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **A build machine has no store, so it may not judge a seat.** The set is data the control plane
|
||||||
|
// owns (novox/hq ADR 0122), and `ParseManifest` runs on the build machine too, against whatever set
|
||||||
|
// that binary was compiled with. When the two disagreed, a valid holder of the bus seat was refused
|
||||||
|
// mid-rollout — the compiled row said the seat delivered one provision, the store's row said
|
||||||
|
// another, and the build failed on the copy rather than the truth. The parser judges the manifest;
|
||||||
|
// the seat set judges the claim, where it is loaded.
|
||||||
|
func TestTheParserDoesNotJudgeWhatOnlyTheStoreKnows(t *testing.T) {
|
||||||
|
was := Seats()
|
||||||
|
t.Cleanup(func() { UseSeats(was) })
|
||||||
|
|
||||||
|
// A store whose bus seat delivers something this module does provide.
|
||||||
|
UseSeats([]Seat{{Name: "mesh-broker", Scope: ScopeMesh, Delivers: "amqp", Decision: "test"}})
|
||||||
|
raw := []byte(`{"module":"lavinmq","version":"1",` +
|
||||||
|
`"provides":[{"name":"amqp","scope":"mesh"}],` +
|
||||||
|
`"claims":[{"name":"mesh-broker","scope":"mesh"}]}`)
|
||||||
|
|
||||||
|
m, err := ParseManifest(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("the parser refused a claim only the seat set can judge: %v", err)
|
||||||
|
}
|
||||||
|
if got := CatalogueProblems(Shelf{m.Module: m}); len(got) != 0 {
|
||||||
|
t.Fatalf("a holder that provides what the store says the seat delivers was refused: %v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And with the store saying the seat delivers something else, registration is what refuses it.
|
||||||
|
UseSeats([]Seat{{Name: "mesh-broker", Scope: ScopeMesh, Delivers: "mesh-bus", Decision: "test"}})
|
||||||
|
if _, err := ParseManifest(raw); err != nil {
|
||||||
|
t.Fatalf("the parser judged it the second time: %v", err)
|
||||||
|
}
|
||||||
|
got := strings.Join(CatalogueProblems(Shelf{m.Module: m}), "; ")
|
||||||
|
if !strings.Contains(got, `does not provide "mesh-bus"`) {
|
||||||
|
t.Fatalf("registration did not refuse a holder that cannot answer for the seat: %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -138,26 +138,32 @@ func TestAModuleDefinesAndClaimsItsOwnSeat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **At registration, not in the parser** (novox/hq ADR 0122): a seat's scope is a property of the
|
||||||
|
// set, the set is the store's, and the parser also runs on a build machine that has no store.
|
||||||
func TestASeatClaimedAtAnotherScopeIsRefused(t *testing.T) {
|
func TestASeatClaimedAtAnotherScopeIsRefused(t *testing.T) {
|
||||||
_, err := ParseManifest(claimed(`[{"name":"npm-package-registry","scope":"node"}]`))
|
m, err := ParseManifest(claimed(`[{"name":"npm-package-registry","scope":"node"}]`))
|
||||||
if err == nil {
|
if err != nil {
|
||||||
t.Fatal("a mesh seat was held per node")
|
t.Fatalf("the parser judged a scope it reads from data it may not have: %v", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "mesh seat") {
|
got := strings.Join(CatalogueProblems(Shelf{m.Module: m}), "; ")
|
||||||
t.Fatalf("the refusal does not say which scope the seat is: %v", err)
|
if !strings.Contains(got, "mesh seat") {
|
||||||
|
t.Fatalf("the refusal does not say which scope the seat is: %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestADeliveringSeatIsOnlyHeldByAModuleThatProvides(t *testing.T) {
|
func TestADeliveringSeatIsOnlyHeldByAModuleThatProvides(t *testing.T) {
|
||||||
// Holding it makes the module the mesh's answer for the provision. A module that cannot answer
|
// Holding it makes the module the mesh's answer for the provision. A module that cannot answer
|
||||||
// would be the answer anyway, and every consumer would be sent to it.
|
// would be the answer anyway, and every consumer would be sent to it.
|
||||||
|
// And refused at registration, where the seat set is the store's: what a seat delivers is
|
||||||
|
// data, so a compiled copy of it may not be what refuses a build (novox/hq ADR 0122).
|
||||||
raw := []byte(`{"module":"thing","version":"1","claims":[{"name":"git","scope":"mesh"}]}`)
|
raw := []byte(`{"module":"thing","version":"1","claims":[{"name":"git","scope":"mesh"}]}`)
|
||||||
_, err := ParseManifest(raw)
|
m, err := ParseManifest(raw)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
t.Fatal("a module holding the git seat need not provide git")
|
t.Fatalf("the parser judged what a seat delivers: %v", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), `does not provide "git"`) {
|
got := strings.Join(CatalogueProblems(Shelf{m.Module: m}), "; ")
|
||||||
t.Fatalf("the refusal does not say what is missing: %v", err)
|
if !strings.Contains(got, `does not provide "git"`) {
|
||||||
|
t.Fatalf("the refusal does not say what is missing: %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@
|
|||||||
"on": [
|
"on": [
|
||||||
{
|
{
|
||||||
"arg": "GO_BASE",
|
"arg": "GO_BASE",
|
||||||
"image": "golang@sha256:1ae0735f00daffa3aaf1363a5184c0d2dc55c78e3db4ec70241cdac97bf84b59"
|
"image": "golang@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user