Merge pull request 'A module's bus account not issued yet is foreseen in a node's answer, so a new module can be assigned; a refusal leads with each held module's first reason (issue 774)' (#141) from fix/kept-read-and-foreseen-account into main
This commit was merged in pull request #141.
This commit is contained in:
@@ -257,28 +257,6 @@ func (c *composer) wouldMakeSecretForModule(node Node, module, name string) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// busCredentialIssued refuses an own secret called `broker` whose bus account nobody issued (novox/hq issue 203): the broker
|
|
||||||
// credential is never invented, an account on the bus minted by `module issue`.
|
|
||||||
func (c *composer) busCredentialIssued(node, module, name string) error {
|
|
||||||
var user string
|
|
||||||
switch name {
|
|
||||||
case "broker":
|
|
||||||
user = node + "." + module
|
|
||||||
case ProvisionerSecret:
|
|
||||||
user = "provisioner." + node
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !slices.Contains(c.in.BusMinted, user) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"%s on %s has no bus credential: nothing was issued for %s, and a push "+
|
|
||||||
"would seal a placeholder its process cannot read (novox/hq issue 203). "+
|
|
||||||
"`module issue %s --node %s`, then push again",
|
|
||||||
module, node, user, module, node)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// givenUnderAnOldKey is the refusal for a value a person gave sealed to a key the machine no longer holds.
|
// givenUnderAnOldKey is the refusal for a value a person gave sealed to a key the machine no longer holds.
|
||||||
func givenUnderAnOldKey(module, node, name string) error {
|
func givenUnderAnOldKey(module, node, name string) error {
|
||||||
return fmt.Errorf("%s on %s holds %q, which was given to the mesh rather than made by it, and %s has "+
|
return fmt.Errorf("%s on %s holds %q, which was given to the mesh rather than made by it, and %s has "+
|
||||||
@@ -354,10 +332,10 @@ func (c *composer) ownSecretsFor(node Node, plan catalogue.Resolution, handing c
|
|||||||
}
|
}
|
||||||
if !held && mode == Foreseeing {
|
if !held && mode == Foreseeing {
|
||||||
// Asked ahead of the send: what the send would do about it, by the send's own rules. Made by it -- a stand-in,
|
// Asked ahead of the send: what the send would do about it, by the send's own rules. Made by it -- a stand-in,
|
||||||
// named; refused by it -- refused here, the same words (novox/hq issue 275).
|
// named; refused by it -- refused here, the same words (novox/hq issue 275). A bus account not yet issued is
|
||||||
if err := c.busCredentialIssued(node.Name, m.Module, name); err != nil {
|
// foreseen, never refused: the vault mints it from the grants part's wanted list once the placement is written
|
||||||
return nil, nil, err
|
// on the operator's Approve (ADR 0561 §1, ADR 0567 §5), so a composition asked ahead of that cannot hold it
|
||||||
}
|
// (issue 774, 2026-10-30: every proposal placing mesh-conditions, mesh-self-check or mesh-healers refused).
|
||||||
if err := c.wouldMakeSecretForModule(node, m.Module, name); err != nil {
|
if err := c.wouldMakeSecretForModule(node, m.Module, name); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,34 +116,32 @@ func TestAnOutsideSecretIsPlacedOnlyOnceAPersonGaveIt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1364/AC3 (the send's refusals, novox/hq issue 203 and 275): in the Foreseeing mode a bus credential nobody issued and a given secret
|
// 1364/AC3 (the send's refusals, novox/hq issue 275): in the Foreseeing mode a given secret under an old key is refused in the
|
||||||
// under an old key are refused in the controller's words; with the credential issued, the secret is foreseen. No secret value is in
|
// controller's words. No secret value is in an error. A bus account not yet issued is foreseen, issued or not (issue 774: the vault
|
||||||
// an error.
|
// mints it once the placement is written on the Approve, ADR 0561 §1, ADR 0567 §5; mesh-self-check's proposal on ace was refused
|
||||||
|
// "has no bus credential: nothing was issued for ace.mesh-self-check").
|
||||||
func TestTheSendsRefusalsAreRefusedInTheSendsWords(t *testing.T) {
|
func TestTheSendsRefusalsAreRefusedInTheSendsWords(t *testing.T) {
|
||||||
broker := catalogue.Manifest{Module: "gate", Version: "1",
|
broker := catalogue.Manifest{Module: "mesh-self-check", Version: "1", RunsAs: "mesh-self-check",
|
||||||
OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/var/lib/mesh/gate/broker", Taken: catalogue.TakenAtStart}}}
|
OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/var/lib/mesh/mesh-self-check/broker", Taken: catalogue.TakenAtStart}}}
|
||||||
in := withAssistant(t)
|
in := withAssistant(t)
|
||||||
in.Catalogue["gate"] = broker
|
in.Catalogue["mesh-self-check"] = broker
|
||||||
laptop := nodeOf(t, &in, "laptop")
|
laptop := nodeOf(t, &in, "laptop")
|
||||||
laptop.Assigned = append(laptop.Assigned, "gate")
|
laptop.Assigned = append(laptop.Assigned, "mesh-self-check")
|
||||||
_, err := Render(in, "laptop", Foreseeing)
|
for _, minted := range [][]string{nil, {"laptop.mesh-self-check"}} {
|
||||||
want := "gate on laptop has no bus credential: nothing was issued for laptop.gate, and a push would seal a placeholder " +
|
in.BusMinted = minted
|
||||||
"its process cannot read (novox/hq issue 203). `module issue gate --node laptop`, then push again"
|
got, err := Render(in, "laptop", Foreseeing)
|
||||||
if err == nil || err.Error() != want {
|
if err != nil || !got.Rendering.Foreseen["mesh-self-check"]["broker"] {
|
||||||
t.Fatalf("got %v, want %q", err, want)
|
t.Fatalf("minted %v: the bus account is not foreseen: %v", minted, err)
|
||||||
}
|
}
|
||||||
// Reading refuses nothing for it: the machine is not running it.
|
// Reading refuses nothing for it: the machine is not running it.
|
||||||
if _, err := Render(in, "laptop", Reading); err != nil {
|
if _, err := Render(in, "laptop", Reading); err != nil {
|
||||||
t.Fatalf("Reading refused a secret the machine is not running: %v", err)
|
t.Fatalf("Reading refused a secret the machine is not running: %v", err)
|
||||||
}
|
}
|
||||||
in.BusMinted = []string{"laptop.gate"}
|
|
||||||
if got := rendered(t, in, "laptop", Foreseeing); !got.Rendering.Foreseen["gate"]["broker"] {
|
|
||||||
t.Errorf("an issued bus credential is not foreseen: %v", got.Rendering.Foreseen)
|
|
||||||
}
|
}
|
||||||
// A given secret under an old key.
|
// A given secret under an old key.
|
||||||
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "accepted", SealedTo: "an-old-key", Sealed: secretValue},
|
in.OwnSecrets = []OwnSecretRow{{Node: "laptop", Module: "assistant", Name: "password", Origin: "accepted", SealedTo: "an-old-key", Sealed: secretValue},
|
||||||
{Node: "laptop", Module: "gate", Name: "broker", Origin: "made", SealedTo: laptop.SealingKey, Sealed: secretValue}}
|
{Node: "laptop", Module: "mesh-self-check", Name: "broker", Origin: "made", SealedTo: laptop.SealingKey, Sealed: secretValue}}
|
||||||
_, err = Render(in, "laptop", Foreseeing)
|
_, err := Render(in, "laptop", Foreseeing)
|
||||||
wantOld := "assistant on laptop holds \"password\", which was given to the mesh rather than made by it, and laptop has since generated a new " +
|
wantOld := "assistant on laptop holds \"password\", which was given to the mesh rather than made by it, and laptop has since generated a new " +
|
||||||
"sealing key. The mesh cannot make another; issue it again"
|
"sealing key. The mesh cannot make another; issue it again"
|
||||||
if err == nil || err.Error() != wantOld {
|
if err == nil || err.Error() != wantOld {
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ func composeEach(in compose.Input, node string, fps map[string]string, applied b
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
module, leave := culprit(in, node, fps, mode, failed, err)
|
module, leave := culprit(in, node, fps, mode, failed, err)
|
||||||
if module == "" {
|
if module == "" {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, firstReason(err, failed)
|
||||||
}
|
}
|
||||||
hold(module, err.Error(), leave)
|
hold(module, err.Error(), leave)
|
||||||
continue
|
continue
|
||||||
@@ -373,6 +373,23 @@ func composeEach(in compose.Input, node string, fps map[string]string, applied b
|
|||||||
return out, skipped, stood, nil
|
return out, skipped, stood, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// firstReason is err, a refusal no culprit is found for, led by the first reason of each held module it names: a held
|
||||||
|
// module's stand-in refused later never replaces why it was held (issue 774, 2026-10-30: mesh-self-check on ace held for
|
||||||
|
// "has no bus credential", then refused as "runs as its own account and declares no own secret broker", its bare stub's
|
||||||
|
// words, which were all the proposal's refusal said).
|
||||||
|
func firstReason(err error, failed map[string]string) error {
|
||||||
|
var named []string
|
||||||
|
for _, m := range sortedKeys(failed) {
|
||||||
|
if strings.Contains(err.Error(), m) {
|
||||||
|
named = append(named, m+" is held: "+failed[m])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(named) == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s; its stand-in is then refused: %w", strings.Join(named, "; "), err)
|
||||||
|
}
|
||||||
|
|
||||||
// blames says the composition with m's stub (again) makes m the culprit of err: it composes, or fails otherwise. The
|
// blames says the composition with m's stub (again) makes m the culprit of err: it composes, or fails otherwise. The
|
||||||
// runtime's stub is never a runtime (no build is taken in, so it carries no bundle) and always fails otherwise: it is
|
// runtime's stub is never a runtime (no build is taken in, so it carries no bundle) and always fails otherwise: it is
|
||||||
// the culprit only where its stub composes (issue 774, 2026-10-30 19:30: node-tools held for any error naming it).
|
// the culprit only where its stub composes (issue 774, 2026-10-30 19:30: node-tools held for any error naming it).
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ func TestInput(t *testing.T) {
|
|||||||
t.Run("a node refused whole says what it held", aNodeRefusedWholeSaysWhatItHeld)
|
t.Run("a node refused whole says what it held", aNodeRefusedWholeSaysWhatItHeld)
|
||||||
t.Run("the runtime is blamed only where its stub composes", theRuntimeIsBlamedOnlyWhereItsStubComposes)
|
t.Run("the runtime is blamed only where its stub composes", theRuntimeIsBlamedOnlyWhereItsStubComposes)
|
||||||
t.Run("a running module waits for its build", aRunningModuleWaitsForItsBuild)
|
t.Run("a running module waits for its build", aRunningModuleWaitsForItsBuild)
|
||||||
|
t.Run("a held module's first reason leads", aHeldModulesFirstReasonLeads)
|
||||||
t.Run("another node not resolved is named", otherNodeNotResolvedIsNamed)
|
t.Run("another node not resolved is named", otherNodeNotResolvedIsNamed)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
xterm, err := os.ReadFile("../../catalogue/testdata/catalogue-835bf22b-xterm.module.json")
|
xterm, err := os.ReadFile("../../catalogue/testdata/catalogue-835bf22b-xterm.module.json")
|
||||||
|
|||||||
@@ -131,3 +131,23 @@ func rawStrings(rs []json.RawMessage) []string {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 774: a refusal of a held module's stand-in leads with why the module was held, its first reason, as mesh-self-check's
|
||||||
|
// on ace should have; a refusal naming no held module is as it was.
|
||||||
|
func aHeldModulesFirstReasonLeads(t *testing.T) {
|
||||||
|
held := map[string]string{"mesh-self-check": "mesh-self-check on ace has no bus credential: nothing was issued for ace.mesh-self-check"}
|
||||||
|
for _, row := range []struct {
|
||||||
|
err, want string
|
||||||
|
}{
|
||||||
|
{"mesh-self-check runs as its own account and declares no own secret broker",
|
||||||
|
"mesh-self-check is held: mesh-self-check on ace has no bus credential: nothing was issued for ace.mesh-self-check; " +
|
||||||
|
"its stand-in is then refused: mesh-self-check runs as its own account and declares no own secret broker"},
|
||||||
|
{"ace is the hub and has no endpoint", "ace is the hub and has no endpoint"},
|
||||||
|
} {
|
||||||
|
err := errors.New(row.err)
|
||||||
|
got := firstReason(err, held)
|
||||||
|
if got.Error() != row.want || !errors.Is(got, err) {
|
||||||
|
t.Errorf("refused %q, want %q", got, row.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user