Werkdock skeleton: doctor, load, run over the bwrap engine (Go)
The minimal build-capable CLI decided in RFC 0002's outcome: stdlib-only Go module, one static binary. - engine: RunSpec behind the Engine interface (RFC 0001); the Bwrap engine ports Werkator's hardened invocation — uid-0 mapping, read-only rootfs at /, proc/dev/tmp/root before the user binds so binds below them land inside, mountpoint pre-creation in the rootfs including file mountpoints, and a guard against binds escaping the rootfs. --clearenv gives docker-style clean environments (HOME/PATH set explicitly). - store: images under $WERKDOCK_HOME (default ~/.werkdock), load unpacks via the tar CLI into a tmp dir and renames atomically. - cli: docker-shaped run flags (-v/-e/-w/--rm); refused docker flags (-p, --network, --memory, --cpus, --user, -d) fail loudly with the reason; exit codes follow docker (125 CLI errors, child code through). - doctor: port of werkator-build-prerequisites.sh — userns probe with the three signals, tar/zstd, free space and group-quota headroom via testable df/quota parsers, same PASS/FAIL output. - tests: argv golden test, mountpoint and escape tests, flag refusals, store round trip, doctor parsers — plus real-sandbox integration tests that skip where bwrap or userns are unavailable. Also records in step 21: RFC 0002 levels 2/3 deferred; next goal is sandbox builds of Werkator, Werkbaum, and Werkdock itself. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
de79210a7b
commit
e4bfeacf5a
@@ -0,0 +1,57 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"werkdock/internal/doctor"
|
||||
"werkdock/internal/store"
|
||||
)
|
||||
|
||||
func doctorCmd(args []string) int {
|
||||
fs := flag.NewFlagSet("doctor", flag.ContinueOnError)
|
||||
fs.SetOutput(io.Discard)
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
targetDir := ""
|
||||
switch len(fs.Args()) {
|
||||
case 0:
|
||||
st, err := store.Default()
|
||||
if err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
targetDir = st.Root
|
||||
// The store may not exist yet; measure its closest existing
|
||||
// ancestor, which sits on the same filesystem.
|
||||
for {
|
||||
if _, err := os.Stat(targetDir); err == nil {
|
||||
break
|
||||
}
|
||||
parent := filepath.Dir(targetDir)
|
||||
if parent == targetDir {
|
||||
break
|
||||
}
|
||||
targetDir = parent
|
||||
}
|
||||
case 1:
|
||||
targetDir = fs.Args()[0]
|
||||
default:
|
||||
return fail(fmt.Errorf("unexpected argument %q", fs.Args()[1]))
|
||||
}
|
||||
report := doctor.Run(targetDir, os.Getuid(), runCombined)
|
||||
report.Render(os.Stdout)
|
||||
if report.OK() {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func runCombined(name string, args ...string) (string, error) {
|
||||
out, err := exec.Command(name, args...).CombinedOutput()
|
||||
return string(out), err
|
||||
}
|
||||
Reference in New Issue
Block a user