Files

100 lines
2.4 KiB
Go

package tentacle
import (
"context"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
)
func TestDelegationHelper(t *testing.T) {
if len(os.Args) < 3 {
return
}
mode, path := os.Args[len(os.Args)-2], os.Args[len(os.Args)-1]
if mode == "delegation-guardian" {
if err := RunGuardian(os.Stdin, os.Stdout, path); err != nil {
os.Exit(1)
}
os.Exit(0)
}
if mode != "delegation-owner" {
return
}
executable, _ := os.Executable()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
delegation, err := StartDelegation(ctx, []string{executable, "-test.run=^TestDelegationHelper$", "--", "delegation-guardian"})
if err != nil {
t.Fatal(err)
}
manager, err := NewDelegatedManager(delegation)
if err != nil {
t.Fatal(err)
}
session, err := manager.Start(ctx, child_spec(t, "spawn", "spawn"))
if err != nil {
t.Fatal(err)
}
select {
case <-session.Ready():
case <-ctx.Done():
t.Fatal(ctx.Err())
}
if err := session.Activate(); err != nil {
t.Fatal(err)
}
result, err := session.Invoke(ctx, "echo", nil)
if err != nil {
t.Fatal(err)
}
value := <-result
if value.Err != nil {
t.Fatal(value.Err)
}
var pid int
if err := json.Unmarshal(value.Value, &pid); err != nil || pid <= 0 {
t.Fatalf("descendant: %s %v", value.Value, err)
}
if err := os.WriteFile(path, []byte(delegation.Parent), 0600); err != nil {
t.Fatal(err)
}
// Simulate abrupt reader death: no Stop, Shutdown, Close, or deferred cleanup.
os.Exit(0)
}
func TestNativeDelegationReaderDeath(t *testing.T) {
if os.Getenv("CTHULHU_TENTACLE_MANAGED") != "1" {
t.Skip("requires a systemd user manager and cgroup v2")
}
executable, _ := os.Executable()
marker := filepath.Join(t.TempDir(), "group")
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
command := exec.CommandContext(ctx, executable, "-test.run=^TestDelegationHelper$", "--", "delegation-owner", marker)
if output, err := command.CombinedOutput(); err != nil {
t.Fatalf("owner: %v %s", err, output)
}
parent, err := os.ReadFile(marker)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(6 * time.Second)
defer timer.Stop()
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
for {
if _, err := os.Stat(string(parent)); os.IsNotExist(err) {
return
}
select {
case <-ticker.C:
case <-timer.C:
t.Fatal("reader death left its service/descendants alive")
}
}
}