package resolution import ( "fmt" "path" "strings" mcat "git.novox.be/novox/mesh-sdk/go/catalogue" ) // declaredPaths is every machine path a module declares it owns: a resource's literal path, and a file // the mesh renders for it outside the operator's home. An access is not here — reading is not owning. // // **Cleaned, because the machine writes the cleaned path**: /etc//hosts, /etc/./hosts, /etc/../etc/hosts and // /etc/hosts/ are all /etc/hosts to it, and a seat's path is clean by its own rule, so a declaration compared // as written would pass for a path nobody named. func declaredPaths(m mcat.Manifest) map[string]bool { out := map[string]bool{} if m.OwnsPaths != nil { // Carried: what its manifest declares, never what runs of it (issue 1126, seed-8: f1-circuits, snake and txt-game // on ace owned their /var/lib paths on the node and none on the controller). for _, p := range m.OwnsPaths { out[p] = true } return out } for _, r := range m.Resources { if p, ok := r["path"].(string); ok && strings.HasPrefix(p, "/") { out[path.Clean(p)] = true } } for _, f := range m.Facts { if !f.Home && strings.HasPrefix(f.Path, "/") { out[path.Clean(f.Path)] = true } } return out } // DeclaredPaths is every machine path a module's manifest declares it owns (declaredPaths), sorted and never nil: what // a carried module owns (Manifest.OwnsPaths). func DeclaredPaths(m mcat.Manifest) []string { return append([]string{}, sortedKeys(declaredPaths(m))...) } // declaresPath says whether a module declares one path, as a resource or as a file the mesh renders. func declaresPath(m mcat.Manifest, p string) bool { return declaredPaths(m)[p] } // pathSeats is which seats name each path: the mesh's own, and then those modules declare, in the order // given. A module's seat naming a path a seat already names loses it, and the refusal says so (novox/hq // ADR 0287 — as for a seat's name, the second declaration loses). func pathSeats(order []string, shelf mcat.Shelf, declaredBy map[string]string) (map[string][]string, []string) { named := map[string][]string{} for _, s := range mcat.Seats() { for _, p := range s.Paths { named[p.Path] = append(named[p.Path], s.Name) } } var problems []string for _, module := range order { for _, s := range shelf[module].DefinesSeats { if declaredBy != nil && declaredBy[s.Name] != module { continue // a second declaration of the seat's name, refused for that already } for _, p := range s.Paths { if others := named[p]; len(others) > 0 { problems = append(problems, fmt.Sprintf( "%s declares the seat %s naming the path %q, which %s already names; no two seats "+ "name one path (novox/hq ADR 0287)", module, s.Name, p, strings.Join(others, " and "))) continue } named[p] = append(named[p], s.Name) } } } return named, problems } // unclaimedSeatPaths is each path a module declares that a seat names while it claims none of the seats // naming it (novox/hq ADR 0287): only a claimant declares a seat's path, and a module claiming any one of // them may. func unclaimedSeatPaths(m mcat.Manifest, named map[string][]string) []string { claims := map[string]bool{} for _, c := range m.Claims { claims[canonicalSeat(c.Name)] = true } var problems []string declared := declaredPaths(m) for _, p := range sortedKeys(declared) { seats := named[p] if len(seats) == 0 { continue } claimsOne := false for _, s := range seats { claimsOne = claimsOne || claims[s] } if !claimsOne { problems = append(problems, fmt.Sprintf( "%s declares the path %q, which the seat %s names, and claims none of them; only a holder of "+ "a seat naming a path declares it (novox/hq ADR 0287)", m.Module, p, strings.Join(seats, " and "))) } } return problems } // seatPathsOnMachine is each path the modules of one machine declare that a seat names, for a module // claiming none of the seats naming it (novox/hq ADR 0287). Registration refuses the same of a manifest; // this holds what the controller generates for a machine to it too, because a computed module's resources // exist only once composed (checkResources is asked them there). The seats are the mesh's own and those the // machine's modules declare. func seatPathsOnMachine(modules []mcat.Manifest) []string { shelf := mcat.Shelf{} var order []string for _, m := range modules { shelf[m.Module] = m order = append(order, m.Module) } named, _ := pathSeats(order, shelf, nil) var problems []string for _, m := range modules { problems = append(problems, unclaimedSeatPaths(m, named)...) } return problems }