aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-01 17:00:46 +0900
committerOphestra <cat@gensokyo.uk>2026-10-01 21:44:30 +0900
commit6a144f780ab62a5e6936d8f551dd03f51b11a153 (patch)
treed3bb149739945becd2d0330d678f7ada620953f4
parenta356dceedf0be29ae584866e540872ec1c048f59 (diff)
test/sharefs: migrate tests
This runs tests directly in the container, significantly reducing overhead introduced by virtualisation. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--.gitea/workflows/test.yml2
-rw-r--r--internal/workflows/doc.go9
-rw-r--r--internal/workflows/step.go22
-rw-r--r--internal/workflows/test.go35
-rw-r--r--test/flake.nix2
-rw-r--r--test/internal/testsuite/testsuite.go40
-rw-r--r--test/options.md1175
-rw-r--r--test/sharefs/configuration.nix44
-rw-r--r--test/sharefs/default.nix44
-rw-r--r--test/sharefs/main.go126
-rw-r--r--test/sharefs/test.py60
11 files changed, 226 insertions, 1333 deletions
diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml
index de11bccc..9cc6d82e 100644
--- a/.gitea/workflows/test.yml
+++ b/.gitea/workflows/test.yml
@@ -1 +1 @@
-{"name":"Test","on":["push"],"jobs":{"hakurei":{"name":"Hakurei","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-vm-output","path":"result/*","retention-days":1}}]},"race":{"name":"Hakurei (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-race-vm-output","path":"result/*","retention-days":1}}]},"sandbox":{"name":"Sandbox","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-vm-output","path":"result/*","retention-days":1}}]},"sandbox-race":{"name":"Sandbox (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox-race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-race-vm-output","path":"result/*","retention-days":1}}]},"sharefs":{"name":"ShareFS","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sharefs"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sharefs-vm-output","path":"result/*","retention-days":1}}]},"check":{"name":"Flake checks","needs":["hakurei","race","sandbox","sandbox-race","sharefs"],"runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run checks","run":"nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test"}]},"dist":{"name":"Create distribution","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Request distribution","id":"dist-test","run":"export HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Upload distribution","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-${{ steps.dist-test.outputs.rev }}","path":"result/*","retention-days":1}}]}}}
+{"name":"Test","on":["push"],"jobs":{"hakurei":{"name":"Hakurei","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.hakurei"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-vm-output","path":"result/*","retention-days":1}}]},"race":{"name":"Hakurei (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-race-vm-output","path":"result/*","retention-days":1}}]},"sandbox":{"name":"Sandbox","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-vm-output","path":"result/*","retention-days":1}}]},"sandbox-race":{"name":"Sandbox (race detector)","runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run NixOS test","run":"nix build --out-link result --print-out-paths --print-build-logs ./test#checks.x86_64-linux.sandbox-race"},{"name":"Upload test output","uses":"actions/upload-artifact@v3","with":{"name":"sandbox-race-vm-output","path":"result/*","retention-days":1}}]},"sharefs":{"name":"ShareFS","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Set up Go toolchain","uses":"actions/setup-go@v6","with":{"go-version-file":"go.mod"}},{"name":"install fuse and fs_mark","run":"apt-get update \u0026\u0026 apt-get install -y fuse3 fsmark"},{"name":"Request distribution","id":"dist","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Install hakurei","run":"HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" \u0026\u0026 tar xf \"result/hakurei-$HAKUREI_VERSION-amd64.tar.gz\" \u0026\u0026 \"./hakurei-$HAKUREI_VERSION-amd64/install.sh\" \u0026\u0026 sudo -u ubuntu hakurei version"},{"name":"Mount sharefs","run":"useradd -ru 1023 -md /var/lib/sdcard -k /var/empty -s /sbin/nologin media_rw \u0026\u0026 install -dm0 /sdcard \u0026\u0026 sharefs -o rw,noexec,nosuid,nodev,noatime,allow_other,mkdir,source=/var/lib/sdcard,setuid=1023,setgid=1023 /sdcard"},{"name":"Compile and run test suite","run":"sharefs -V \u0026\u0026 rm -rf result \u0026\u0026 go run -tags=testsuite ./test/sharefs ubuntu"}]},"check":{"name":"Flake checks","needs":["hakurei","race","sandbox","sandbox-race"],"runs-on":"nix","steps":[{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Run checks","run":"nix --print-build-logs --experimental-features 'nix-command flakes' flake check ./test"}]},"dist":{"name":"Create distribution","runs-on":"rosa","steps":[{"name":"Fix container filesystem","run":"rm /var/run \u0026\u0026 ln -sf ../run /var"},{"name":"Checkout","uses":"actions/checkout@v4"},{"name":"Request distribution","id":"dist-test","run":"HAKUREI_REV=\"$(git rev-parse --short HEAD)\" \u0026\u0026 /rosa/bin/mbf ci dist -o result . \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" \u0026\u0026 echo \"rev=$HAKUREI_REV\" \u003e\u003e \"$GITHUB_OUTPUT\""},{"name":"Upload distribution","uses":"actions/upload-artifact@v3","with":{"name":"hakurei-${{ steps.dist-test.outputs.rev }}","path":"result/*","retention-days":1}}]}}}
diff --git a/internal/workflows/doc.go b/internal/workflows/doc.go
index 0c7e94b9..e34c59c8 100644
--- a/internal/workflows/doc.go
+++ b/internal/workflows/doc.go
@@ -47,6 +47,7 @@ The container can be specified via docker-compose:
runner:
restart: always
image: docker.io/gitea/act_runner:nightly
+ network_mode: host
environment:
CONFIG_FILE: /config.yaml
GITEA_INSTANCE_URL: "https://git.gensokyo.uk"
@@ -62,17 +63,21 @@ The container can be specified via docker-compose:
Before starting the container, configure act_runner via config.yaml:
container:
- network: host
options: -e PAGER=cat -e MBF_CACHE_DIR=/rosa -e MBF_POISON_OPEN=1
-v /var/lib/rosa:/rosa
+ --security-opt='unmask=/proc/*'
+ --cap-add=SYS_ADMIN
--device=/dev/kvm
+ --device=/dev/fuse
valid_volumes:
- /var/lib/rosa
where /var/lib/rosa is the absolute pathname of the cache directory in the init
namespace. Setting MBF_POISON_OPEN enables cmd/mbf to run as root. It is also
a good idea here to set runner.capacity to reflect the capacity of the guest, so
-jobs can be consumed quicker.
+jobs can be consumed quicker. Removing mount points covering /proc enables
+testing of cmd/hakurei. Exposing the fuse device and adding capability SYS_ADMIN
+enables testing of cmd/sharefs.
Build a statically-linked cmd/mbf:
diff --git a/internal/workflows/step.go b/internal/workflows/step.go
index 423a48ff..219ed78b 100644
--- a/internal/workflows/step.go
+++ b/internal/workflows/step.go
@@ -12,6 +12,16 @@ var fixup = Step{
// checkout is the standard actions/checkout step.
var checkout = Step{Name: "Checkout", Uses: "actions/checkout@v4"}
+// toolchain is a step for setting up the Go toolchain.
+var toolchain = Step{
+ Name: "Set up Go toolchain",
+ Uses: "actions/setup-go@v6",
+
+ With: []KV[any]{
+ {"go-version-file", "go.mod"},
+ },
+}
+
// newUploadArtifact returns an actions/upload-artifact step uploading
// everything in the result directory as the specified name.
func newUploadArtifact(display, name string) Step {
@@ -33,13 +43,23 @@ func newCIRequest(display, name, id string) Step {
return Step{
Name: "Request " + display,
ID: id,
- Run: "export HAKUREI_REV=\"$(git rev-parse --short HEAD)\" && " +
+ Run: "HAKUREI_REV=\"$(git rev-parse --short HEAD)\" && " +
"/rosa/bin/mbf ci " + name + " " +
". \"$(cat cmd/dist/VERSION)-$HAKUREI_REV\" && " +
"echo \"rev=$HAKUREI_REV\" >> \"$GITHUB_OUTPUT\"",
}
}
+// install is a step to unpack and deploy a hakurei distribution created by a
+// step with identifier dist.
+var install = Step{
+ Name: "Install hakurei",
+ Run: "HAKUREI_VERSION=\"$(cat cmd/dist/VERSION)-${{ steps.dist.outputs.rev }}\" && " +
+ "tar xf \"result/hakurei-$HAKUREI_VERSION-amd64.tar.gz\" && " +
+ "\"./hakurei-$HAKUREI_VERSION-amd64/install.sh\" && " +
+ "sudo -u ubuntu hakurei version",
+}
+
// newNixOSTest returns a step for running the named NixOS test.
func newNixOSTest(name string) Step {
return Step{
diff --git a/internal/workflows/test.go b/internal/workflows/test.go
index 88e91a22..28307b53 100644
--- a/internal/workflows/test.go
+++ b/internal/workflows/test.go
@@ -53,12 +53,40 @@ var _ = (&Workflow{
{"sharefs", Job{
Name: "ShareFS",
- On: "nix",
+ On: "rosa",
Steps: []Step{
+ fixup,
checkout,
- newNixOSTest("sharefs"),
- newUploadArtifact("test output", "sharefs-vm-output"),
+ toolchain,
+
+ {
+ Name: "install fuse and fs_mark",
+ Run: "apt-get update && apt-get install -y fuse3 fsmark",
+ },
+
+ newCIRequest("distribution", "dist -o result", "dist"),
+ install,
+
+ {
+ Name: "Mount sharefs",
+ Run: "useradd " +
+ "-ru 1023 " +
+ "-md /var/lib/sdcard " +
+ "-k /var/empty " +
+ "-s /sbin/nologin " +
+ "media_rw && install -dm0 /sdcard && " +
+ "sharefs " +
+ "-o rw,noexec,nosuid,nodev,noatime," +
+ "allow_other,mkdir,source=/var/lib/sdcard," +
+ "setuid=1023,setgid=1023 /sdcard",
+ },
+
+ {
+ Name: "Compile and run test suite",
+ Run: "sharefs -V && rm -rf result && " +
+ "go run -tags=testsuite ./test/sharefs ubuntu",
+ },
},
}},
@@ -71,7 +99,6 @@ var _ = (&Workflow{
"race",
"sandbox",
"sandbox-race",
- "sharefs",
},
Steps: []Step{
diff --git a/test/flake.nix b/test/flake.nix
index 01a07f78..b106ec7a 100644
--- a/test/flake.nix
+++ b/test/flake.nix
@@ -55,8 +55,6 @@
withRace = true;
};
- sharefs = callPackage ./sharefs { inherit system self; };
-
formatting = runCommandLocal "check-formatting" { nativeBuildInputs = [ nixfmt ]; } ''
cd ${./.}
diff --git a/test/internal/testsuite/testsuite.go b/test/internal/testsuite/testsuite.go
new file mode 100644
index 00000000..4b70da34
--- /dev/null
+++ b/test/internal/testsuite/testsuite.go
@@ -0,0 +1,40 @@
+// Package testsuite provides many quick-and-dirty integration testing utilities.
+package testsuite
+
+import (
+ "log"
+ "os"
+ "os/exec"
+ "os/signal"
+ "syscall"
+)
+
+func init() {
+ log.SetFlags(0)
+ log.SetPrefix("testsuite: ")
+
+ if os.Geteuid() != 0 {
+ log.Fatal("this program must run as root")
+ }
+}
+
+// ReceiveSignals blocks until a termination signal arrives, and terminates.
+func ReceiveSignals() {
+ s := make(chan os.Signal, 3)
+ signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
+ log.Fatalf("terminating on signal %s", <-s)
+}
+
+// MustRun runs command and terminates the testsuite on error.
+func MustRun(command ...string) {
+ cmd := exec.Command(command[0], command[1:]...)
+ cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
+ if err := cmd.Run(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+// MustRunAs wraps [MustRun] for sudo.
+func MustRunAs(username string, command ...string) {
+ MustRun(append([]string{"sudo", "-u", username}, command...)...)
+}
diff --git a/test/options.md b/test/options.md
deleted file mode 100644
index 6f6acddd..00000000
--- a/test/options.md
+++ /dev/null
@@ -1,1175 +0,0 @@
-## environment\.hakurei\.enable
-
-
-
-Whether to enable hakurei\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.package
-
-
-
-The hakurei package to use\.
-
-
-
-*Type:*
-package
-
-
-
-*Default:*
-
-```nix
-<derivation hakurei-static-x86_64-unknown-linux-musl-0.4.3>
-```
-
-
-
-## environment\.hakurei\.apps
-
-Declaratively configured hakurei apps\.
-
-
-
-*Type:*
-attribute set of (submodule)
-
-
-
-*Default:*
-
-```nix
-{ }
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.enablements\.dbus
-
-
-
-Whether to proxy D-Bus\.
-
-
-
-*Type:*
-null or boolean
-
-
-
-*Default:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.enablements\.pipewire
-
-
-
-Whether to share the PipeWire server via pipewire-pulse on a SecurityContext socket\.
-
-
-
-*Type:*
-null or boolean
-
-
-
-*Default:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.enablements\.wayland
-
-
-
-Whether to share the Wayland server via security-context-v1\.
-
-
-
-*Type:*
-null or boolean
-
-
-
-*Default:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.enablements\.x11
-
-
-
-Whether to share the X11 socket and allow connection\.
-
-
-
-*Type:*
-null or boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.packages
-
-
-
-List of extra packages to install via home-manager\.
-
-
-
-*Type:*
-list of package
-
-
-
-*Default:*
-
-```nix
-[ ]
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.args
-
-
-
-Custom args\.
-Setting this to null will default to script name\.
-
-
-
-*Type:*
-null or (list of string)
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.command
-
-
-
-Command to run as the target user\.
-Setting this to null will default command to launcher name\.
-Has no effect when script is set\.
-
-
-
-*Type:*
-null or string
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.dbus\.session
-
-
-
-D-Bus session bus custom configuration\.
-Setting this to null will enable built-in defaults\.
-
-
-
-*Type:*
-null or (function that evaluates to a(n) anything)
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.dbus\.system
-
-
-
-D-Bus system bus custom configuration\.
-Setting this to null will disable the system bus proxy\.
-
-
-
-*Type:*
-null or anything
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.devel
-
-
-
-Whether to enable debugging-related kernel interfaces\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.device
-
-
-
-Whether to enable access to all devices\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.env
-
-
-
-Environment variables to set for the initial process in the sandbox\.
-
-
-
-*Type:*
-null or (attribute set of string)
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.extraConfig
-
-
-
-Extra home-manager configuration\.
-
-
-
-*Type:*
-anything
-
-
-
-*Default:*
-
-```nix
-{ }
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.extraPaths
-
-
-
-Extra paths to make available to the container\.
-
-
-
-*Type:*
-list of attribute set of anything
-
-
-
-*Default:*
-
-```nix
-[ ]
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.gpu
-
-
-
-Target process GPU and driver access\.
-Setting this to null will enable GPU whenever X or Wayland is enabled\.
-
-
-
-*Type:*
-null or boolean
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.groups
-
-
-
-List of groups to inherit from the privileged user\.
-
-
-
-*Type:*
-list of string
-
-
-
-*Default:*
-
-```nix
-[ ]
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.hostAbstract
-
-
-
-Whether to enable share abstract unix socket scope\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.hostNet
-
-
-
-Whether to enable share host net namespace\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-true
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.identity
-
-
-
-Application identity\. Identity 0 is reserved for system services\.
-
-
-
-*Type:*
-integer between 1 and 9999 (both inclusive)
-
-
-
-## environment\.hakurei\.apps\.\<name>\.insecureWayland
-
-
-
-Whether to enable direct access to the Wayland socket\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.mapRealUid
-
-
-
-Whether to enable mapping to priv-user uid\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.multiarch
-
-
-
-Whether to enable multiarch kernel-level support\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.name
-
-
-
-Name of the app’s launcher script\.
-
-
-
-*Type:*
-string
-
-
-
-## environment\.hakurei\.apps\.\<name>\.nix
-
-
-
-Whether to enable nix daemon access\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.path
-
-
-
-Custom executable path\.
-Setting this to null will default to the start script\.
-
-
-
-*Type:*
-null or string
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.schedPolicy
-
-
-
-Scheduling policy to set for the container\.
-The zero value retains the current scheduling policy\.
-
-
-
-*Type:*
-null or one of “fifo”, “rr”, “batch”, “idle”, “deadline”, “ext”
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.schedPriority
-
-
-
-Scheduling priority to set for the container\.
-
-
-
-*Type:*
-null or integer between 1 and 99 (both inclusive)
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.script
-
-
-
-Application launch script\.
-
-
-
-*Type:*
-null or string
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.share
-
-
-
-Package containing share files\.
-Setting this to null will default package name to wrapper name\.
-
-
-
-*Type:*
-null or package
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.shareRuntime
-
-
-
-Whether to enable sharing of XDG_RUNTIME_DIR between containers under the same identity\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.shareTmpdir
-
-
-
-Whether to enable sharing of TMPDIR between containers under the same identity\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.shareUid
-
-
-
-Whether to enable sharing identity with another application\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.tty
-
-
-
-Whether to enable access to the controlling terminal\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.useCommonPaths
-
-
-
-Whether to enable common extra paths\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-true
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.userns
-
-
-
-Whether to enable user namespace creation\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.verbose
-
-
-
-Whether to enable launchers with verbose output\.
-
-
-
-*Type:*
-boolean
-
-
-
-*Default:*
-
-```nix
-false
-```
-
-
-
-*Example:*
-
-```nix
-true
-```
-
-
-
-## environment\.hakurei\.apps\.\<name>\.wait_delay
-
-
-
-Duration to wait for after interrupting a container’s initial process in nanoseconds\.
-A negative value causes the container to be terminated immediately on cancellation\.
-Setting this to null defaults to five seconds\.
-
-
-
-*Type:*
-null or signed integer
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.commonPaths
-
-
-
-Common extra paths to make available to the container\.
-
-
-
-*Type:*
-list of attribute set of anything
-
-
-
-*Default:*
-
-```nix
-[ ]
-```
-
-
-
-## environment\.hakurei\.extraHomeConfig
-
-
-
-Extra home-manager configuration to merge with all target users\.
-
-
-
-*Type:*
-anything
-
-
-
-## environment\.hakurei\.hsuPackage
-
-
-
-The hsu package to use\.
-
-
-
-*Type:*
-package
-
-
-
-*Default:*
-
-```nix
-<derivation hakurei-hsu-0.4.3>
-```
-
-
-
-## environment\.hakurei\.sharefs\.package
-
-
-
-The sharefs package to use\.
-
-
-
-*Type:*
-package
-
-
-
-*Default:*
-
-```nix
-<derivation sharefs>
-```
-
-
-
-## environment\.hakurei\.sharefs\.group
-
-
-
-Name of the group to run the sharefs daemon as\.
-
-
-
-*Type:*
-string
-
-
-
-*Default:*
-
-```nix
-"sharefs"
-```
-
-
-
-## environment\.hakurei\.sharefs\.name
-
-
-
-Host path to mount sharefs on\.
-
-
-
-*Type:*
-string
-
-
-
-*Default:*
-
-```nix
-"/sdcard"
-```
-
-
-
-## environment\.hakurei\.sharefs\.source
-
-
-
-Writable backing directory\. Setting this to null disables sharefs\.
-
-
-
-*Type:*
-null or string
-
-
-
-*Default:*
-
-```nix
-null
-```
-
-
-
-## environment\.hakurei\.sharefs\.user
-
-
-
-Name of the user to run the sharefs daemon as\.
-
-
-
-*Type:*
-string
-
-
-
-*Default:*
-
-```nix
-"sharefs"
-```
-
-
-
-## environment\.hakurei\.shell
-
-
-
-Absolute path to preferred shell\.
-
-
-
-*Type:*
-string
-
-
-
-*Default:*
-
-```nix
-"/run/current-system/sw/bin/bash"
-```
-
-
-
-## environment\.hakurei\.stateDir
-
-
-
-The state directory where app home directories are stored\.
-
-
-
-*Type:*
-string
-
-
-
-## environment\.hakurei\.users
-
-
-
-Users allowed to spawn hakurei apps and their corresponding hakurei identity\.
-
-
-
-*Type:*
-attribute set of integer between 0 and 99 (both inclusive)
-
-
-
-*Default:*
-
-```nix
-{ }
-```
-
-
diff --git a/test/sharefs/configuration.nix b/test/sharefs/configuration.nix
deleted file mode 100644
index 05d67dcb..00000000
--- a/test/sharefs/configuration.nix
+++ /dev/null
@@ -1,44 +0,0 @@
-{ pkgs, ... }:
-{
- users.users = {
- alice = {
- isNormalUser = true;
- description = "Alice Foobar";
- password = "foobar";
- uid = 1000;
- };
- };
-
- home-manager.users.alice.home.stateVersion = "24.11";
-
- # Automatically login on tty1 as a normal user:
- services.getty.autologinUser = "alice";
-
- environment = {
- # For benchmarking sharefs:
- systemPackages = [ pkgs.fsmark ];
- };
-
- virtualisation = {
- # Hopefully reduces spurious test failures:
- memorySize = if pkgs.stdenv.hostPlatform.is32bit then 2046 else 8192;
-
- diskSize = 6 * 1024;
-
- qemu.options = [
- # Increase test performance:
- "-smp 16"
- ];
- };
-
- environment.hakurei = rec {
- enable = true;
- stateDir = "/var/lib/hakurei";
- sharefs.source = "${stateDir}/sdcard";
- users.alice = 0;
-
- extraHomeConfig = {
- home.stateVersion = "23.05";
- };
- };
-}
diff --git a/test/sharefs/default.nix b/test/sharefs/default.nix
deleted file mode 100644
index e963eaa6..00000000
--- a/test/sharefs/default.nix
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- testers,
-
- system,
- self,
-}:
-testers.nixosTest {
- name = "sharefs";
- nodes.machine =
- { options, pkgs, ... }:
- let
- fhs =
- let
- hakurei = options.environment.hakurei.package.default;
- in
- pkgs.buildFHSEnv {
- pname = "hakurei-fhs";
- inherit (hakurei) version;
- targetPkgs = _: hakurei.targetPkgs;
- extraOutputsToInstall = [ "dev" ];
- profile = ''
- export PKG_CONFIG_PATH="/usr/share/pkgconfig:$PKG_CONFIG_PATH"
- '';
- };
- in
- {
- environment.systemPackages = [
- # For go tests:
- (pkgs.writeShellScriptBin "sharefs-workload-hakurei-tests" ''
- cp -r "${self.packages.${system}.hakurei.src}" "/sdcard/hakurei" && cd "/sdcard/hakurei"
- ${fhs}/bin/hakurei-fhs -c 'ROSA_SKIP_BINFMT=1 CC="clang -O3 -Werror" go test ./...'
- '')
- ];
-
- imports = [
- ./configuration.nix
-
- self.nixosModules.hakurei
- self.inputs.home-manager.nixosModules.home-manager
- ];
- };
-
- testScript = builtins.readFile ./test.py;
-}
diff --git a/test/sharefs/main.go b/test/sharefs/main.go
new file mode 100644
index 00000000..536a61b3
--- /dev/null
+++ b/test/sharefs/main.go
@@ -0,0 +1,126 @@
+//go:build testsuite
+
+// The sharefs test program checks cli behaviour and exercises the filesystem
+// implemented by cmd/sharefs using fs_mark.
+package main
+
+import (
+ "errors"
+ "log"
+ "os"
+ "os/exec"
+ "slices"
+ "strings"
+
+ "hakurei.app/test/internal/testsuite"
+)
+
+// checkBadOpts invokes cmd/sharefs with the specified options and compares
+// the resulting error message.
+func checkBadOpts(username, opts, want string) {
+ var buf strings.Builder
+ buf.Grow(len(want))
+
+ sudo := []string{"sudo", "-u", username, "-i", "--"}
+ if username == "root" {
+ sudo = nil
+ }
+
+ a := slices.Concat(sudo, []string{
+ "sharefs",
+ "-f",
+ "-o", "source=/etc," + opts,
+ "/mnt",
+ })
+ cmd := exec.Command(a[0], a[1:]...)
+ cmd.Stderr = &buf
+ err := cmd.Run()
+ if err == nil {
+ log.Fatalf("opts=%q, unexpected success", opts)
+ }
+ if e, ok := errors.AsType[*exec.ExitError](err); !ok {
+ log.Fatal(err)
+ } else if !e.Exited() {
+ log.Fatal(e)
+ }
+
+ if got := buf.String(); got != want {
+ log.Fatalf("opts=%q\n\t got:%q\n\twant:%q", opts, got, want)
+ }
+}
+
+func main() {
+ go testsuite.ReceiveSignals()
+
+ if err := os.Mkdir("result", 0755); err != nil {
+ log.Fatal(err)
+ }
+
+ if len(os.Args) != 2 {
+ log.Fatal("expecting 1 argument")
+ }
+ username := os.Args[1]
+
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+
+ testsuite.MustRun(
+ "fs_mark",
+ "-v",
+ "-d", "/sdcard/fs_mark",
+ "-l", "result/fs_mark.log",
+ )
+ }()
+
+ log.Println("checking malformed setuid/setgid representation")
+ checkBadOpts(username, "setuid=ff", "sharefs: invalid value for option setuid\n")
+ checkBadOpts(username, "setgid=ff", "sharefs: invalid value for option setgid\n")
+
+ log.Println("checking bounds check for setuid/setgid")
+ checkBadOpts(username, "setuid=0", "sharefs: invalid value for option setuid\n")
+ checkBadOpts(username, "setgid=0", "sharefs: invalid value for option setgid\n")
+ checkBadOpts(username, "setuid=-1", "sharefs: invalid value for option setuid\n")
+ checkBadOpts(username, "setgid=-1", "sharefs: invalid value for option setgid\n")
+
+ log.Println("checking non-root setuid/setgid")
+ checkBadOpts(username, "setuid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n")
+ checkBadOpts(username, "setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n")
+ checkBadOpts(username, "setuid=1023,setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n")
+ checkBadOpts(username, "mkdir", "sharefs: mkdir has no effect when not starting as root\n")
+
+ log.Println("checking root without setuid/setgid")
+ checkBadOpts("root", "allow_other", "sharefs: setuid and setgid must not be 0\n")
+ checkBadOpts("root", "setuid=1023", "sharefs: setuid and setgid must not be 0\n")
+ checkBadOpts("root", "setgid=1023", "sharefs: setuid and setgid must not be 0\n")
+
+ log.Println("verifying mount point")
+ if err := os.Remove("/mnt"); err != nil {
+ log.Fatal(err)
+ }
+
+ log.Println("checking unprivileged mount/unmount")
+ testsuite.MustRunAs(username, "-i", "mkdir", "/tmp/sdcard", "/tmp/persistent")
+ testsuite.MustRunAs(username, "-i", "sharefs", "-o", "source=/tmp/persistent", "/tmp/sdcard")
+ testsuite.MustRunAs(username, "-i", "touch", "/tmp/sdcard/check")
+ testsuite.MustRunAs(username, "-i", "umount", "/tmp/sdcard")
+ testsuite.MustRunAs(username, "-i", "rm", "/tmp/persistent/check")
+ testsuite.MustRunAs(username, "-i", "rmdir", "/tmp/sdcard", "/tmp/persistent")
+
+ log.Println("waiting for fs_mark to complete")
+ <-done
+
+ const (
+ backingDir = "/var/lib/sdcard"
+ sharefsUser = "media_rw"
+ )
+ log.Println("checking permissions")
+ testsuite.MustRunAs(sharefsUser, "touch", backingDir+"/fs_mark/.check")
+ testsuite.MustRunAs(sharefsUser, "rm", backingDir+"/fs_mark/.check")
+ testsuite.MustRunAs(username, "-i", "rm", "-rf", "/sdcard/fs_mark")
+ if _, err := os.ReadDir(backingDir + "/fs_mark"); err == nil {
+ log.Fatal("fs_mark directory was not removed")
+ } else if !errors.Is(err, os.ErrNotExist) {
+ log.Fatal(err)
+ }
+}
diff --git a/test/sharefs/test.py b/test/sharefs/test.py
deleted file mode 100644
index 4b925c9c..00000000
--- a/test/sharefs/test.py
+++ /dev/null
@@ -1,60 +0,0 @@
-start_all()
-machine.wait_for_unit("multi-user.target")
-
-# To check sharefs version:
-print(machine.succeed("sharefs -V"))
-
-# Make sure sharefs started:
-machine.wait_for_unit("sdcard.mount")
-
-machine.succeed("mkdir /mnt")
-def check_bad_opts_output(opts, want, source="/etc", privileged=False):
- output = machine.fail(("" if privileged else "sudo -u alice -i ") + f"sharefs -f -o source={source},{opts} /mnt 2>&1")
- if output != want:
- raise Exception(f"unexpected output: {output}")
-
-# Malformed setuid/setgid representation:
-check_bad_opts_output("setuid=ff", "sharefs: invalid value for option setuid\n")
-check_bad_opts_output("setgid=ff", "sharefs: invalid value for option setgid\n")
-
-# Bounds check for setuid/setgid:
-check_bad_opts_output("setuid=0", "sharefs: invalid value for option setuid\n")
-check_bad_opts_output("setgid=0", "sharefs: invalid value for option setgid\n")
-check_bad_opts_output("setuid=-1", "sharefs: invalid value for option setuid\n")
-check_bad_opts_output("setgid=-1", "sharefs: invalid value for option setgid\n")
-
-# Non-root setuid/setgid:
-check_bad_opts_output("setuid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n")
-check_bad_opts_output("setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n")
-check_bad_opts_output("setuid=1023,setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n")
-check_bad_opts_output("mkdir", "sharefs: mkdir has no effect when not starting as root\n")
-
-# Starting as root without setuid/setgid:
-check_bad_opts_output("allow_other", "sharefs: setuid and setgid must not be 0\n", privileged=True)
-check_bad_opts_output("setuid=1023", "sharefs: setuid and setgid must not be 0\n", privileged=True)
-check_bad_opts_output("setgid=1023", "sharefs: setuid and setgid must not be 0\n", privileged=True)
-
-# Make sure nothing actually got mounted:
-machine.fail("umount /mnt")
-machine.succeed("rmdir /mnt")
-
-# Unprivileged mount/unmount:
-machine.succeed("sudo -u alice -i mkdir /home/alice/{sdcard,persistent}")
-machine.succeed("sudo -u alice -i sharefs -o source=/home/alice/persistent /home/alice/sdcard")
-machine.succeed("sudo -u alice -i touch /home/alice/sdcard/check")
-machine.succeed("sudo -u alice -i umount /home/alice/sdcard")
-machine.succeed("sudo -u alice -i rm /home/alice/persistent/check")
-machine.succeed("sudo -u alice -i rmdir /home/alice/{sdcard,persistent}")
-
-# Benchmark sharefs:
-machine.succeed("fs_mark -v -d /sdcard/fs_mark -l /tmp/fs_log.txt")
-machine.copy_from_vm("/tmp/fs_log.txt", "")
-
-# Check permissions:
-machine.succeed("sudo -u sharefs touch /var/lib/hakurei/sdcard/fs_mark/.check")
-machine.succeed("sudo -u sharefs rm /var/lib/hakurei/sdcard/fs_mark/.check")
-machine.succeed("sudo -u alice rm -rf /sdcard/fs_mark")
-machine.fail("ls /var/lib/hakurei/sdcard/fs_mark")
-
-# Run hakurei tests on sharefs:
-machine.succeed("sudo -u alice -i sharefs-workload-hakurei-tests")