hst: support ephemeral overlay mounts
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m41s
Test / ShareFS (push) Successful in 3m47s
Test / Hakurei (push) Successful in 3m59s
Test / Sandbox (race detector) (push) Successful in 5m33s
Test / Hakurei (race detector) (push) Successful in 6m38s
Test / Flake checks (push) Successful in 1m13s
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m41s
Test / ShareFS (push) Successful in 3m47s
Test / Hakurei (push) Successful in 3m59s
Test / Sandbox (race detector) (push) Successful in 5m33s
Test / Hakurei (race detector) (push) Successful in 6m38s
Test / Flake checks (push) Successful in 1m13s
This is useful for reusing a readonly template without autoroot. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -31,6 +31,8 @@ func (e AbsoluteError) Is(target error) bool {
|
|||||||
type Absolute struct{ pathname unique.Handle[string] }
|
type Absolute struct{ pathname unique.Handle[string] }
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
_ fmt.GoStringer = new(Absolute)
|
||||||
|
|
||||||
_ encoding.TextAppender = new(Absolute)
|
_ encoding.TextAppender = new(Absolute)
|
||||||
_ encoding.TextMarshaler = new(Absolute)
|
_ encoding.TextMarshaler = new(Absolute)
|
||||||
_ encoding.TextUnmarshaler = new(Absolute)
|
_ encoding.TextUnmarshaler = new(Absolute)
|
||||||
@@ -40,6 +42,10 @@ var (
|
|||||||
_ encoding.BinaryUnmarshaler = new(Absolute)
|
_ encoding.BinaryUnmarshaler = new(Absolute)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (a *Absolute) GoString() string {
|
||||||
|
return fmt.Sprintf("check.MustAbs(%q)", a.String())
|
||||||
|
}
|
||||||
|
|
||||||
// ok returns whether [Absolute] is not the zero value.
|
// ok returns whether [Absolute] is not the zero value.
|
||||||
func (a *Absolute) ok() bool { return a != nil && *a != (Absolute{}) }
|
func (a *Absolute) ok() bool { return a != nil && *a != (Absolute{}) }
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ type Ops interface {
|
|||||||
Bind(source, target *check.Absolute, flags int) Ops
|
Bind(source, target *check.Absolute, flags int) Ops
|
||||||
// Overlay appends an op that mounts the overlay pseudo filesystem.
|
// Overlay appends an op that mounts the overlay pseudo filesystem.
|
||||||
Overlay(target, state, work *check.Absolute, layers ...*check.Absolute) Ops
|
Overlay(target, state, work *check.Absolute, layers ...*check.Absolute) Ops
|
||||||
|
// OverlayEphemeral appends a MountOverlayOp with an ephemeral upperdir and workdir.
|
||||||
|
OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) Ops
|
||||||
// OverlayReadonly appends an op that mounts the overlay pseudo filesystem readonly.
|
// OverlayReadonly appends an op that mounts the overlay pseudo filesystem readonly.
|
||||||
OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) Ops
|
OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) Ops
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -3,6 +3,7 @@ package hst_test
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -283,11 +284,11 @@ func checkFs(t *testing.T, testCases []fsTestCase) {
|
|||||||
if !reflect.DeepEqual(ops, &tc.ops) {
|
if !reflect.DeepEqual(ops, &tc.ops) {
|
||||||
gotString := new(strings.Builder)
|
gotString := new(strings.Builder)
|
||||||
for _, op := range *ops {
|
for _, op := range *ops {
|
||||||
gotString.WriteString("\n" + op.String())
|
gotString.WriteString("\n" + fmt.Sprintf("%#v", op))
|
||||||
}
|
}
|
||||||
wantString := new(strings.Builder)
|
wantString := new(strings.Builder)
|
||||||
for _, op := range tc.ops {
|
for _, op := range tc.ops {
|
||||||
wantString.WriteString("\n" + op.String())
|
wantString.WriteString("\n" + fmt.Sprintf("%#v", op))
|
||||||
}
|
}
|
||||||
t.Errorf("Apply: %s, want %s", gotString, wantString)
|
t.Errorf("Apply: %s, want %s", gotString, wantString)
|
||||||
}
|
}
|
||||||
@@ -339,6 +340,10 @@ func (p opsAdapter) Overlay(target, state, work *check.Absolute, layers ...*chec
|
|||||||
return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
|
return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p opsAdapter) OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
|
||||||
|
return opsAdapter{p.Ops.OverlayEphemeral(target, layers...)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
|
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
|
||||||
return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
|
return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-9
@@ -2,6 +2,7 @@ package hst
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"hakurei.app/check"
|
"hakurei.app/check"
|
||||||
@@ -40,7 +41,7 @@ func (o *FSOverlay) Valid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Upper != nil { // rw
|
if o.Upper != nil { // rw
|
||||||
return o.Work != nil && len(o.Lower) > 0
|
return o.Work != nil || len(o.Lower) > 0
|
||||||
} else { // ro
|
} else { // ro
|
||||||
return len(o.Lower) >= 2
|
return len(o.Lower) >= 2
|
||||||
}
|
}
|
||||||
@@ -58,8 +59,11 @@ func (o *FSOverlay) Host() []*check.Absolute {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p := make([]*check.Absolute, 0, 2+len(o.Lower))
|
p := make([]*check.Absolute, 0, 2+len(o.Lower))
|
||||||
if o.Upper != nil && o.Work != nil {
|
if o.Upper != nil {
|
||||||
p = append(p, o.Upper, o.Work)
|
p = append(p, o.Upper)
|
||||||
|
if o.Work != nil {
|
||||||
|
p = append(p, o.Work)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p = append(p, o.Lower...)
|
p = append(p, o.Lower...)
|
||||||
return p
|
return p
|
||||||
@@ -70,11 +74,18 @@ func (o *FSOverlay) Apply(z *ApplyState) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.Upper != nil && o.Work != nil {
|
if o.Upper != nil {
|
||||||
z.Overlay(o.Target, o.Upper, o.Work, o.Lower...)
|
|
||||||
if o.Target.Is(fhs.AbsRoot) {
|
if o.Target.Is(fhs.AbsRoot) {
|
||||||
z.NoRemountRoot = true
|
z.NoRemountRoot = true
|
||||||
}
|
}
|
||||||
|
if o.Work != nil {
|
||||||
|
z.Overlay(o.Target, o.Upper, o.Work, o.Lower...)
|
||||||
|
} else {
|
||||||
|
z.OverlayEphemeral(o.Target, slices.Concat(
|
||||||
|
o.Lower,
|
||||||
|
[]*check.Absolute{o.Upper})...,
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
z.OverlayReadonly(o.Target, o.Lower...)
|
z.OverlayReadonly(o.Target, o.Lower...)
|
||||||
}
|
}
|
||||||
@@ -90,12 +101,19 @@ func (o *FSOverlay) String() string {
|
|||||||
lower[i] = check.EscapeOverlayDataSegment(a.String())
|
lower[i] = check.EscapeOverlayDataSegment(a.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.Upper != nil && o.Work != nil {
|
if o.Upper != nil {
|
||||||
return "w*" + strings.Join(append([]string{
|
if o.Work != nil {
|
||||||
|
return "w*" + strings.Join(append([]string{
|
||||||
|
check.EscapeOverlayDataSegment(o.Target.String()),
|
||||||
|
check.EscapeOverlayDataSegment(o.Upper.String()),
|
||||||
|
check.EscapeOverlayDataSegment(o.Work.String())},
|
||||||
|
lower...), check.SpecialOverlayPath)
|
||||||
|
}
|
||||||
|
return "e*" + strings.Join(append([]string{
|
||||||
check.EscapeOverlayDataSegment(o.Target.String()),
|
check.EscapeOverlayDataSegment(o.Target.String()),
|
||||||
check.EscapeOverlayDataSegment(o.Upper.String()),
|
check.EscapeOverlayDataSegment(o.Upper.String())},
|
||||||
check.EscapeOverlayDataSegment(o.Work.String())},
|
|
||||||
lower...), check.SpecialOverlayPath)
|
lower...), check.SpecialOverlayPath)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return "*" + strings.Join(append([]string{
|
return "*" + strings.Join(append([]string{
|
||||||
check.EscapeOverlayDataSegment(o.Target.String())},
|
check.EscapeOverlayDataSegment(o.Target.String())},
|
||||||
|
|||||||
+13
-1
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"hakurei.app/check"
|
"hakurei.app/check"
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
|
"hakurei.app/fhs"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ func TestFSOverlay(t *testing.T) {
|
|||||||
checkFs(t, []fsTestCase{
|
checkFs(t, []fsTestCase{
|
||||||
{"nil", (*hst.FSOverlay)(nil), false, nil, nil, nil, "<invalid>"},
|
{"nil", (*hst.FSOverlay)(nil), false, nil, nil, nil, "<invalid>"},
|
||||||
{"nil lower", &hst.FSOverlay{Target: m("/etc"), Lower: []*check.Absolute{nil}}, false, nil, nil, nil, "<invalid>"},
|
{"nil lower", &hst.FSOverlay{Target: m("/etc"), Lower: []*check.Absolute{nil}}, false, nil, nil, nil, "<invalid>"},
|
||||||
{"zero lower", &hst.FSOverlay{Target: m("/etc"), Upper: m("/"), Work: m("/")}, false, nil, nil, nil, "<invalid>"},
|
{"zero lower", &hst.FSOverlay{Target: m("/etc"), Work: m("/")}, false, nil, nil, nil, "<invalid>"},
|
||||||
{"zero lower ro", &hst.FSOverlay{Target: m("/etc")}, false, nil, nil, nil, "<invalid>"},
|
{"zero lower ro", &hst.FSOverlay{Target: m("/etc")}, false, nil, nil, nil, "<invalid>"},
|
||||||
{"short lower", &hst.FSOverlay{Target: m("/etc"), Lower: ms("/etc")}, false, nil, nil, nil, "<invalid>"},
|
{"short lower", &hst.FSOverlay{Target: m("/etc"), Lower: ms("/etc")}, false, nil, nil, nil, "<invalid>"},
|
||||||
|
|
||||||
@@ -62,5 +63,16 @@ func TestFSOverlay(t *testing.T) {
|
|||||||
Work: m("/tmp/work"),
|
Work: m("/tmp/work"),
|
||||||
}}, m("/"), ms("/tmp/upper", "/tmp/work", "/tmp/.src0", "/tmp/.src1"),
|
}}, m("/"), ms("/tmp/upper", "/tmp/work", "/tmp/.src0", "/tmp/.src1"),
|
||||||
"w*/:/tmp/upper:/tmp/work:/tmp/.src0:/tmp/.src1"},
|
"w*/:/tmp/upper:/tmp/work:/tmp/.src0:/tmp/.src1"},
|
||||||
|
|
||||||
|
{"ephemeral", &hst.FSOverlay{
|
||||||
|
Target: m("/"),
|
||||||
|
Lower: ms("/tmp/.src0", "/tmp/.src1"),
|
||||||
|
Upper: m("/tmp/upper"),
|
||||||
|
}, true, container.Ops{&container.MountOverlayOp{
|
||||||
|
Target: m("/"),
|
||||||
|
Lower: ms("/tmp/.src0", "/tmp/.src1", "/tmp/upper"),
|
||||||
|
Upper: fhs.AbsRoot,
|
||||||
|
}}, m("/"), ms("/tmp/upper", "/tmp/.src0", "/tmp/.src1"),
|
||||||
|
"e*/:/tmp/upper:/tmp/.src0:/tmp/.src1"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,6 +382,10 @@ func (p opsAdapter) Overlay(target, state, work *check.Absolute, layers ...*chec
|
|||||||
return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
|
return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p opsAdapter) OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
|
||||||
|
return opsAdapter{p.Ops.OverlayEphemeral(target, layers...)}
|
||||||
|
}
|
||||||
|
|
||||||
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
|
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
|
||||||
return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
|
return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user