nox nodes reads placement with the console's answered-by line after it

The console appends which machine answered after the JSON, so the nodes
table decoded nothing and printed the raw answer; read the first value
and keep the trailer.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 10:44:43 +01:00
parent a3e9f2cc0f
commit 9523050a98
2 changed files with 13 additions and 3 deletions
+11 -1
View File
@@ -7,6 +7,7 @@ package main
import (
"encoding/json"
"fmt"
"io"
"sort"
"strconv"
"strings"
@@ -689,9 +690,15 @@ func renderNodes(text string) string {
} `json:"placement"`
} `json:"keys"`
}
if err := json.Unmarshal([]byte(text), &p); err != nil || len(p.Keys) == 0 {
// The console says after the answer which machine answered it ("answered by ace"): kept, below the lines.
dec := json.NewDecoder(strings.NewReader(text))
if err := dec.Decode(&p); err != nil || len(p.Keys) == 0 {
return text
}
after := ""
if rest, err := io.ReadAll(dec.Buffered()); err == nil {
after = strings.TrimSpace(string(rest))
}
names := make([]string, 0, len(p.Keys))
for n := range p.Keys {
names = append(names, n)
@@ -703,6 +710,9 @@ func renderNodes(text string) string {
fmt.Fprintf(&b, "%-10s %-15v %-8v %d modules\n", n, orDash(k.Placement.Decided["address"]),
orDash(k.Placement.Decided["site"]), len(k.Placement.Modules))
}
if after != "" {
b.WriteString(after + "\n")
}
return b.String()
}
+2 -2
View File
@@ -195,10 +195,10 @@ func TestAnswersRefusalsAndSilenceAreSaid(t *testing.T) {
t.Errorf("an error answer: exit %d, stdout %q, stderr %q", code, out, errOut)
}
placement := `{"keys":{"novox":{"placement":{"decided":{"address":"10.10.0.1","site":"hub"},"modules":[{},{}]}},` +
`"ace":{"placement":{"decided":{"address":"10.10.0.2"},"modules":[{}]}}}}`
`"ace":{"placement":{"decided":{"address":"10.10.0.2"},"modules":[{}]}}}}` + "\nanswered by ace"
console(t, map[string]string{"mesh-placement.placement": placement})
if code, out, _ := runLine("nodes"); code != 0 || !strings.HasPrefix(out, "ace ") ||
!strings.Contains(out, "10.10.0.1") || !strings.Contains(out, "2 modules") {
!strings.Contains(out, "10.10.0.1") || !strings.Contains(out, "2 modules") || !strings.HasSuffix(out, "answered by ace\n") {
t.Errorf("nodes: exit %d, %q", code, out)
}
got = console(t, map[string]string{"mesh_search": `{"matches":[]}`})