Paused: ADR 0325 replaces runs-as and the group grant. Kept for reuse: the bounded descriptor walks, the warrant-backed access act (share, create), the access wait and the missing-path waiting state.
123 lines
4.5 KiB
Go
123 lines
4.5 KiB
Go
package apply
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/novox/mesh-host/internal/declaration"
|
|
)
|
|
|
|
// A missing accessed path waits for the operator (novox/hq ADR 0324, issue 567).
|
|
//
|
|
// ADR 0051 refused an access whose path is not there, and the refusal failed the whole apply: one module's
|
|
// folder not made yet held every other module on the machine. The runtime must still not make it — a bind
|
|
// whose source is missing is made by it as root, with whatever mode it picks (04-ISSUES/026) — so the engine
|
|
// mounts nothing for the access: every container that mounts the path or below it is not created, and is said
|
|
// waiting with the access, while the rest of the declaration applies. The access is stated waiting on every look
|
|
// with a wait of kind access, and the operator's warrant for the create act makes the path, each level as its
|
|
// nearest existing ancestor is. Only when that ancestor is a directory: one that is a file or a link is refused
|
|
// as before, and one owned by a group that is never granted is stated unhealthy, with no wait.
|
|
|
|
// MissingPath is an accessed path that is not there, and its nearest existing ancestor.
|
|
type MissingPath struct {
|
|
Ancestor string
|
|
// Levels are the names to make below Ancestor, outermost first.
|
|
Levels []string
|
|
// Info is the ancestor as lstat reads it: a link is not a directory.
|
|
Info os.FileInfo
|
|
}
|
|
|
|
// NearestAncestor reads, for a path that is not there, its nearest existing ancestor by lstat. An error for a
|
|
// path that is there, one not stated exactly, or an ancestor that cannot be read.
|
|
func NearestAncestor(path string) (MissingPath, error) {
|
|
if err := shareablePath(path); err != nil {
|
|
return MissingPath{}, err
|
|
}
|
|
if _, err := os.Lstat(path); err == nil || !errors.Is(err, os.ErrNotExist) {
|
|
return MissingPath{}, fmt.Errorf("%s is there, or cannot be read: %v", path, err)
|
|
}
|
|
var levels []string
|
|
for p := path; p != "/"; p = filepath.Dir(p) {
|
|
levels = append([]string{filepath.Base(p)}, levels...)
|
|
parent := filepath.Dir(p)
|
|
info, err := os.Lstat(parent)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return MissingPath{}, err
|
|
}
|
|
return MissingPath{Ancestor: parent, Levels: levels, Info: info}, nil
|
|
}
|
|
return MissingPath{}, errors.New("no part of the path is there")
|
|
}
|
|
|
|
// missingAccess is whether r's path is missing below an ancestor that is a directory: the access waits.
|
|
func missingAccess(path string) (MissingPath, bool) {
|
|
m, err := NearestAncestor(filepath.Clean(path))
|
|
if err != nil || !m.Info.IsDir() {
|
|
return MissingPath{}, false
|
|
}
|
|
return m, true
|
|
}
|
|
|
|
// missingAccesses is every declared access whose path is missing below a directory, by path, with its id: what
|
|
// no container mounts until it is made. A path the guard refuses is not read (issue 339).
|
|
func missingAccesses(resources []declaration.Resource, refused map[string]error) map[string]string {
|
|
out := map[string]string{}
|
|
for _, r := range resources {
|
|
a, ok := r.(*declaration.Access)
|
|
if !ok || refused[filepath.Clean(a.Path)] != nil {
|
|
continue
|
|
}
|
|
if _, missing := missingAccess(a.Path); missing {
|
|
out[filepath.Clean(a.Path)] = a.ID
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// mountsMissing is the access a container waits for: one whose path it mounts, or mounts below, is missing.
|
|
func mountsMissing(c *declaration.Container, missing map[string]string) string {
|
|
for _, v := range c.Volumes {
|
|
src := mountSource(v)
|
|
if !strings.HasPrefix(src, "/") {
|
|
continue
|
|
}
|
|
for path, id := range missing {
|
|
if atOrBelow(filepath.Clean(src), path) {
|
|
return id
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// WaitingOnAccess is every container of the declaration that waits for a missing accessed path, read now: not
|
|
// created by the apply, so not judged alive or dead and not fired on a schedule — the access says why.
|
|
func WaitingOnAccess(d *declaration.Declaration) map[string]bool {
|
|
out := map[string]bool{}
|
|
if d == nil {
|
|
return out
|
|
}
|
|
missing := missingAccesses(d.Resources, refusedPlaces(d.Resources))
|
|
if len(missing) == 0 {
|
|
return out
|
|
}
|
|
for _, r := range d.Resources {
|
|
if c, ok := r.(*declaration.Container); ok && mountsMissing(c, missing) != "" {
|
|
out[c.ID] = true
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// MissingWords is the outcome of an access whose path is missing below a directory.
|
|
func MissingWords(path string) string {
|
|
return fmt.Sprintf("%s is not there yet, below a folder that is: nothing mounts it and the containers that do are "+
|
|
"not created until it is made — on the operator's warrant, by the create act (novox/hq ADR 0324)", path)
|
|
}
|