The restart-on rename reads both list shapes
A resource composed in code carries restart-on as []string; the rename only read []any, so the overlay's registry-trust reload kept its bare reference, pointed at nothing, and the runtime was never restarted — the trust was on disk and not in the daemon, with every check passing. Diagnosed on the built-store-cross-node bed, run 8 (issues 042/048).
This commit is contained in:
@@ -484,16 +484,7 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
|||||||
// the daemon's. Written `<module>.<id>`, and a dot is what marks it as already
|
// the daemon's. Written `<module>.<id>`, and a dot is what marks it as already
|
||||||
// answered: prefixing it again would point at nothing, silently, and the daemon would
|
// answered: prefixing it again would point at nothing, silently, and the daemon would
|
||||||
// serve the old names for ever while everything reported success.
|
// serve the old names for ever while everything reported success.
|
||||||
if reflects, ok := resource["restart-on"].([]any); ok {
|
if renamed := reflectsRenamed(m.Module, resource["restart-on"]); renamed != nil {
|
||||||
var renamed []any
|
|
||||||
for _, id := range reflects {
|
|
||||||
named := fmt.Sprint(id)
|
|
||||||
if strings.Contains(named, ".") {
|
|
||||||
renamed = append(renamed, named)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
renamed = append(renamed, m.Module+"."+named)
|
|
||||||
}
|
|
||||||
copied["restart-on"] = renamed
|
copied["restart-on"] = renamed
|
||||||
}
|
}
|
||||||
out = append(out, copied)
|
out = append(out, copied)
|
||||||
@@ -566,6 +557,36 @@ type Contribution struct {
|
|||||||
// Named after both. Named after the machine alone, two modules on one node wrote to one path: the
|
// Named after both. Named after the machine alone, two modules on one node wrote to one path: the
|
||||||
// second overwrote the first, and the provisioner — reading a directory — saw one consumer where
|
// second overwrote the first, and the provisioner — reading a directory — saw one consumer where
|
||||||
// there were two.
|
// there were two.
|
||||||
|
// reflectsRenamed is a resource's restart-on list under the module's prefix, or nil when it has
|
||||||
|
// none. It reads both the shape JSON parsing produces ([]any) and the shape code composing
|
||||||
|
// resources natively produces ([]string): a reference that was skipped because its list arrived
|
||||||
|
// in the other shape would point at nothing — silently, with the service never restarting and
|
||||||
|
// every check passing, which is how a runtime kept serving without the registry trust its
|
||||||
|
// daemon file already carried.
|
||||||
|
func reflectsRenamed(module string, reflects any) []any {
|
||||||
|
var names []string
|
||||||
|
switch v := reflects.(type) {
|
||||||
|
case []any:
|
||||||
|
for _, id := range v {
|
||||||
|
names = append(names, fmt.Sprint(id))
|
||||||
|
}
|
||||||
|
case []string:
|
||||||
|
names = v
|
||||||
|
}
|
||||||
|
if names == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var renamed []any
|
||||||
|
for _, named := range names {
|
||||||
|
if strings.Contains(named, ".") {
|
||||||
|
renamed = append(renamed, named)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
renamed = append(renamed, module+"."+named)
|
||||||
|
}
|
||||||
|
return renamed
|
||||||
|
}
|
||||||
|
|
||||||
func grantPath(directory, consumer, module string) string {
|
func grantPath(directory, consumer, module string) string {
|
||||||
return strings.TrimRight(directory, "/") + "/" + consumer + "." + module + ".secret"
|
return strings.TrimRight(directory, "/") + "/" + consumer + "." + module + ".secret"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package catalogue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReflectsRenamedReadsBothShapes(t *testing.T) {
|
||||||
|
// The list arrives as []any when the resource was parsed from JSON, and as []string when it
|
||||||
|
// was composed in code — the overlay's registry trust is the second kind. A shape that was
|
||||||
|
// skipped would leave the reference unprefixed, pointing at nothing, and the service would
|
||||||
|
// never restart while every check passed (novox/hq issues 042/048, the run-8 diagnosis).
|
||||||
|
parsed := reflectsRenamed("mesh-wireguard", []any{"registry-trust"})
|
||||||
|
if !reflect.DeepEqual(parsed, []any{"mesh-wireguard.registry-trust"}) {
|
||||||
|
t.Fatalf("parsed shape: %v", parsed)
|
||||||
|
}
|
||||||
|
composed := reflectsRenamed("mesh-wireguard", []string{"registry-trust"})
|
||||||
|
if !reflect.DeepEqual(composed, []any{"mesh-wireguard.registry-trust"}) {
|
||||||
|
t.Fatalf("composed shape: %v", composed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A name already under a module keeps it: that is how a service reflects a file another
|
||||||
|
// module put on the machine.
|
||||||
|
kept := reflectsRenamed("resolver", []string{"mesh-wireguard.fact-node-names", "own-config"})
|
||||||
|
if !reflect.DeepEqual(kept, []any{"mesh-wireguard.fact-node-names", "resolver.own-config"}) {
|
||||||
|
t.Fatalf("dotted name was not kept: %v", kept)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := reflectsRenamed("any", nil); got != nil {
|
||||||
|
t.Fatalf("no list should rename to nothing, got %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user