A collision is two places, not two spellings #71
@@ -221,6 +221,28 @@ func TestPlaceIsValidatedAtTheManifest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTwoModulesPlacedRootsAreNoCollision(t *testing.T) {
|
||||||
|
a := Manifest{Module: "gitea", Resources: []map[string]any{
|
||||||
|
{"id": "state", "type": "directory", "place": "."},
|
||||||
|
{"id": "env", "type": "file", "path": "${dir:state}/server.env", "content": ""},
|
||||||
|
}}
|
||||||
|
b := Manifest{Module: "nextcloud", Resources: []map[string]any{
|
||||||
|
{"id": "state", "type": "directory", "place": "."},
|
||||||
|
{"id": "env", "type": "file", "path": "${dir:state}/server.env", "content": ""},
|
||||||
|
}}
|
||||||
|
if got := checkResources([]Manifest{a, b}); len(got) != 0 {
|
||||||
|
t.Fatalf("alike templates are different places; got %v", got)
|
||||||
|
}
|
||||||
|
// And the real collision is still caught: a module stating another's placed root.
|
||||||
|
c := Manifest{Module: "squatter", Resources: []map[string]any{
|
||||||
|
{"id": "nest", "type": "directory", "path": "/var/lib/gitea"},
|
||||||
|
}}
|
||||||
|
got := checkResources([]Manifest{a, c})
|
||||||
|
if len(got) != 1 || !strings.Contains(got[0], `"/var/lib/gitea"`) {
|
||||||
|
t.Fatalf("a stated path on a placed root collides; got %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func shallowCopy(resource map[string]any) map[string]any {
|
func shallowCopy(resource map[string]any) map[string]any {
|
||||||
copied := map[string]any{}
|
copied := map[string]any{}
|
||||||
for k, v := range resource {
|
for k, v := range resource {
|
||||||
|
|||||||
@@ -705,12 +705,31 @@ func checkResources(modules []Manifest) []string {
|
|||||||
ownedPath := map[string]string{} // path → owning module, for the access check below
|
ownedPath := map[string]string{} // path → owning module, for the access check below
|
||||||
|
|
||||||
for _, m := range modules {
|
for _, m := range modules {
|
||||||
|
// Compared placed, not as written (novox/hq ADR 0112): ${dir:state} is the same six
|
||||||
|
// characters in every module and a different directory in each — two modules' templates
|
||||||
|
// being spelled alike is not two modules owning one path. The default root serves the
|
||||||
|
// comparison: a collision is within one node, and any one root keeps distinct modules'
|
||||||
|
// places distinct. A reference that cannot be placed is left as written — naming what
|
||||||
|
// does not exist is the manifest's own problem, refused where it was made.
|
||||||
|
dirs := dirsFor(m, Rendering{})
|
||||||
for _, r := range m.Resources {
|
for _, r := range m.Resources {
|
||||||
for _, field := range []string{"path", "unit", "name", "package"} {
|
for _, field := range []string{"path", "unit", "name", "package"} {
|
||||||
value, ok := r[field].(string)
|
value, ok := r[field].(string)
|
||||||
if !ok || value == "" {
|
if !ok || value == "" {
|
||||||
|
if field == "path" && fmt.Sprint(r["type"]) == "directory" {
|
||||||
|
// A pathless directory owns its placed path — a module stating that
|
||||||
|
// very path is exactly the collision this exists to catch.
|
||||||
|
value = dirs[fmt.Sprint(r["id"])]
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if field == "path" {
|
||||||
|
if placed, err := dirFill(value, dirs, m.Module); err == nil {
|
||||||
|
value = placed
|
||||||
|
}
|
||||||
|
}
|
||||||
key := field + " " + value
|
key := field + " " + value
|
||||||
if other, taken := owner[key]; taken && other != m.Module {
|
if other, taken := owner[key]; taken && other != m.Module {
|
||||||
problems = append(problems, fmt.Sprintf(
|
problems = append(problems, fmt.Sprintf(
|
||||||
|
|||||||
Reference in New Issue
Block a user