|
|
|
|
@@ -16,7 +16,8 @@ import (
|
|
|
|
|
"strings"
|
|
|
|
|
"syscall"
|
|
|
|
|
"testing"
|
|
|
|
|
_ "unsafe" // for go:linkname
|
|
|
|
|
"time"
|
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"hakurei.app/check"
|
|
|
|
|
"hakurei.app/command"
|
|
|
|
|
@@ -408,37 +409,6 @@ var containerTestCases = []struct {
|
|
|
|
|
func TestContainer(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
t.Run("cancel", testContainerCancel(nil, func(t *testing.T, c *container.Container) {
|
|
|
|
|
wantErr := context.Canceled
|
|
|
|
|
wantExitCode := 0
|
|
|
|
|
if err := c.Wait(); !reflect.DeepEqual(err, wantErr) {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(err); ok {
|
|
|
|
|
t.Error(m)
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("Wait: error = %#v, want %#v", err, wantErr)
|
|
|
|
|
}
|
|
|
|
|
if ps := c.ProcessState(); ps == nil {
|
|
|
|
|
t.Errorf("ProcessState unexpectedly returned nil")
|
|
|
|
|
} else if code := ps.ExitCode(); code != wantExitCode {
|
|
|
|
|
t.Errorf("ExitCode: %d, want %d", code, wantExitCode)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
t.Run("forward", testContainerCancel(func(c *container.Container) {
|
|
|
|
|
c.ForwardCancel = true
|
|
|
|
|
}, func(t *testing.T, c *container.Container) {
|
|
|
|
|
var exitError *exec.ExitError
|
|
|
|
|
if err := c.Wait(); !errors.As(err, &exitError) {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(err); ok {
|
|
|
|
|
t.Error(m)
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("Wait: error = %v", err)
|
|
|
|
|
}
|
|
|
|
|
if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
|
|
|
|
|
t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
for i, tc := range containerTestCases {
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
@@ -563,49 +533,94 @@ func hostnameFromTestCase(name string) string {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testContainerCancel(
|
|
|
|
|
t *testing.T,
|
|
|
|
|
containerExtra func(c *container.Container),
|
|
|
|
|
waitCheck func(t *testing.T, c *container.Container),
|
|
|
|
|
) func(t *testing.T) {
|
|
|
|
|
return func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
|
|
|
waitCheck func(ps *os.ProcessState, waitErr error),
|
|
|
|
|
) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
|
|
|
|
|
|
|
|
c := helperNewContainer(ctx, "block")
|
|
|
|
|
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
|
|
|
|
if containerExtra != nil {
|
|
|
|
|
containerExtra(c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ready := make(chan struct{})
|
|
|
|
|
if r, w, err := os.Pipe(); err != nil {
|
|
|
|
|
t.Fatalf("cannot pipe: %v", err)
|
|
|
|
|
} else {
|
|
|
|
|
c.ExtraFiles = append(c.ExtraFiles, w)
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(ready)
|
|
|
|
|
if _, err = r.Read(make([]byte, 1)); err != nil {
|
|
|
|
|
panic(err.Error())
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := c.Start(); err != nil {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(err); ok {
|
|
|
|
|
t.Fatal(m)
|
|
|
|
|
} else {
|
|
|
|
|
t.Fatalf("cannot start container: %v", err)
|
|
|
|
|
}
|
|
|
|
|
} else if err = c.Serve(); err != nil {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(err); ok {
|
|
|
|
|
t.Error(m)
|
|
|
|
|
} else {
|
|
|
|
|
t.Errorf("cannot serve setup params: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
<-ready
|
|
|
|
|
cancel()
|
|
|
|
|
waitCheck(t, c)
|
|
|
|
|
c := helperNewContainer(ctx, "block")
|
|
|
|
|
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
|
|
|
|
if containerExtra != nil {
|
|
|
|
|
containerExtra(c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ready := make(chan struct{})
|
|
|
|
|
var waitErr error
|
|
|
|
|
r, w, err := os.Pipe()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cannot pipe: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.ExtraFiles = append(c.ExtraFiles, w)
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(ready)
|
|
|
|
|
if _, _err := r.Read(make([]byte, 1)); _err != nil {
|
|
|
|
|
panic(_err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
if err = c.Start(); err != nil {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(err); ok {
|
|
|
|
|
t.Fatal(m)
|
|
|
|
|
} else {
|
|
|
|
|
t.Fatalf("cannot start container: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done := make(chan struct{})
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(done)
|
|
|
|
|
waitErr = c.Wait()
|
|
|
|
|
_ = r.SetReadDeadline(time.Now())
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
if err = c.Serve(); err != nil {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(err); ok {
|
|
|
|
|
t.Error(m)
|
|
|
|
|
} else {
|
|
|
|
|
t.Errorf("cannot serve setup params: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
<-ready
|
|
|
|
|
cancel()
|
|
|
|
|
<-done
|
|
|
|
|
waitCheck(c.ProcessState(), waitErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestForward(t *testing.T) {
|
|
|
|
|
testContainerCancel(t, func(c *container.Container) {
|
|
|
|
|
c.ForwardCancel = true
|
|
|
|
|
}, func(ps *os.ProcessState, waitErr error) {
|
|
|
|
|
var exitError *exec.ExitError
|
|
|
|
|
if !errors.As(waitErr, &exitError) {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(waitErr); ok {
|
|
|
|
|
t.Error(m)
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("Wait: error = %v", waitErr)
|
|
|
|
|
}
|
|
|
|
|
if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
|
|
|
|
|
t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCancel(t *testing.T) {
|
|
|
|
|
testContainerCancel(t, nil, func(ps *os.ProcessState, waitErr error) {
|
|
|
|
|
wantErr := context.Canceled
|
|
|
|
|
if !reflect.DeepEqual(waitErr, wantErr) {
|
|
|
|
|
if m, ok := container.InternalMessageFromError(waitErr); ok {
|
|
|
|
|
t.Error(m)
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("Wait: error = %#v, want %#v", waitErr, wantErr)
|
|
|
|
|
}
|
|
|
|
|
if ps == nil {
|
|
|
|
|
t.Errorf("ProcessState unexpectedly returned nil")
|
|
|
|
|
} else if code := ps.ExitCode(); code != 0 {
|
|
|
|
|
t.Errorf("ExitCode: %d, want %d", code, 0)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestContainerString(t *testing.T) {
|
|
|
|
|
@@ -658,6 +673,58 @@ func init() {
|
|
|
|
|
return fmt.Errorf("gid: %d, want %d", gid, tc.gid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
PR_CAP_AMBIENT = 0x2f
|
|
|
|
|
PR_CAP_AMBIENT_IS_SET = 0x1
|
|
|
|
|
)
|
|
|
|
|
for i := range container.LastCap(nil) {
|
|
|
|
|
r, _, errno := syscall.Syscall(
|
|
|
|
|
syscall.SYS_PRCTL,
|
|
|
|
|
PR_CAP_AMBIENT,
|
|
|
|
|
PR_CAP_AMBIENT_IS_SET,
|
|
|
|
|
i,
|
|
|
|
|
)
|
|
|
|
|
if errno != 0 {
|
|
|
|
|
return os.NewSyscallError("prctl", errno)
|
|
|
|
|
}
|
|
|
|
|
if r != 0 {
|
|
|
|
|
return fmt.Errorf("capability %d is set", i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r, _, errno = syscall.Syscall(
|
|
|
|
|
syscall.SYS_PRCTL,
|
|
|
|
|
syscall.PR_CAPBSET_READ,
|
|
|
|
|
i,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
if errno != 0 {
|
|
|
|
|
return os.NewSyscallError("prctl", errno)
|
|
|
|
|
}
|
|
|
|
|
if r != 0 {
|
|
|
|
|
return fmt.Errorf("capability %d in set", i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const _LINUX_CAPABILITY_VERSION_3 = 0x20080522
|
|
|
|
|
var capData struct {
|
|
|
|
|
effective uint32
|
|
|
|
|
permitted uint32
|
|
|
|
|
inheritable uint32
|
|
|
|
|
}
|
|
|
|
|
if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&struct {
|
|
|
|
|
version uint32
|
|
|
|
|
pid int32
|
|
|
|
|
}{_LINUX_CAPABILITY_VERSION_3, 0})), uintptr(unsafe.Pointer(&capData)), 0); errno != 0 {
|
|
|
|
|
return os.NewSyscallError("capget", errno)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if max(capData.effective, capData.permitted, capData.inheritable) != 0 {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"effective = %d, permitted = %d, inheritable = %d",
|
|
|
|
|
capData.effective, capData.permitted, capData.inheritable,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wantHost := hostnameFromTestCase(tc.name)
|
|
|
|
|
if host, err := os.Hostname(); err != nil {
|
|
|
|
|
return fmt.Errorf("cannot get hostname: %v", err)
|
|
|
|
|
@@ -775,7 +842,7 @@ func TestMain(m *testing.M) {
|
|
|
|
|
}
|
|
|
|
|
c.MustParse(os.Args[1:], func(err error) {
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err.Error())
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
|