container/params: expose pipe
All checks were successful
Test / Create distribution (push) Successful in 26s
Test / Sandbox (push) Successful in 43s
Test / Sandbox (race detector) (push) Successful in 2m12s
Test / Hakurei (push) Successful in 2m22s
Test / Hakurei (race detector) (push) Successful in 3m5s
Test / Hpkg (push) Successful in 3m26s
Test / Flake checks (push) Successful in 1m40s
All checks were successful
Test / Create distribution (push) Successful in 26s
Test / Sandbox (push) Successful in 43s
Test / Sandbox (race detector) (push) Successful in 2m12s
Test / Hakurei (push) Successful in 2m22s
Test / Hakurei (race detector) (push) Successful in 3m5s
Test / Hpkg (push) Successful in 3m26s
Test / Flake checks (push) Successful in 1m40s
This increases flexibility of how caller wants to handle the I/O. Also makes it no longer rely on finalizer. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
6a0ecced90
commit
1196200121
@ -25,6 +25,9 @@ const (
|
|||||||
// CancelSignal is the signal expected by container init on context cancel.
|
// CancelSignal is the signal expected by container init on context cancel.
|
||||||
// A custom [Container.Cancel] function must eventually deliver this signal.
|
// A custom [Container.Cancel] function must eventually deliver this signal.
|
||||||
CancelSignal = SIGUSR2
|
CancelSignal = SIGUSR2
|
||||||
|
|
||||||
|
// Timeout for writing initParams to Container.setup.
|
||||||
|
initSetupTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -37,8 +40,8 @@ type (
|
|||||||
// with behaviour identical to its [exec.Cmd] counterpart.
|
// with behaviour identical to its [exec.Cmd] counterpart.
|
||||||
ExtraFiles []*os.File
|
ExtraFiles []*os.File
|
||||||
|
|
||||||
// param encoder for shim and init
|
// param pipe for shim and init
|
||||||
setup *gob.Encoder
|
setup *os.File
|
||||||
// cancels cmd
|
// cancels cmd
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
// closed after Wait returns
|
// closed after Wait returns
|
||||||
@ -228,10 +231,10 @@ func (p *Container) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// place setup pipe before user supplied extra files, this is later restored by init
|
// place setup pipe before user supplied extra files, this is later restored by init
|
||||||
if fd, e, err := Setup(&p.cmd.ExtraFiles); err != nil {
|
if fd, f, err := Setup(&p.cmd.ExtraFiles); err != nil {
|
||||||
return &StartError{true, "set up params stream", err, false, false}
|
return &StartError{true, "set up params stream", err, false, false}
|
||||||
} else {
|
} else {
|
||||||
p.setup = e
|
p.setup = f
|
||||||
p.cmd.Env = []string{setupEnv + "=" + strconv.Itoa(fd)}
|
p.cmd.Env = []string{setupEnv + "=" + strconv.Itoa(fd)}
|
||||||
}
|
}
|
||||||
p.cmd.ExtraFiles = append(p.cmd.ExtraFiles, p.ExtraFiles...)
|
p.cmd.ExtraFiles = append(p.cmd.ExtraFiles, p.ExtraFiles...)
|
||||||
@ -310,6 +313,9 @@ func (p *Container) Serve() error {
|
|||||||
|
|
||||||
setup := p.setup
|
setup := p.setup
|
||||||
p.setup = nil
|
p.setup = nil
|
||||||
|
if err := setup.SetDeadline(time.Now().Add(initSetupTimeout)); err != nil {
|
||||||
|
return &StartError{true, "set init pipe deadline", err, false, true}
|
||||||
|
}
|
||||||
|
|
||||||
if p.Path == nil {
|
if p.Path == nil {
|
||||||
p.cancel()
|
p.cancel()
|
||||||
@ -324,15 +330,14 @@ func (p *Container) Serve() error {
|
|||||||
p.SeccompRules = make([]seccomp.NativeRule, 0)
|
p.SeccompRules = make([]seccomp.NativeRule, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := setup.Encode(
|
err := gob.NewEncoder(setup).Encode(&initParams{
|
||||||
&initParams{
|
p.Params,
|
||||||
p.Params,
|
Getuid(),
|
||||||
Getuid(),
|
Getgid(),
|
||||||
Getgid(),
|
len(p.ExtraFiles),
|
||||||
len(p.ExtraFiles),
|
p.msg.IsVerbose(),
|
||||||
p.msg.IsVerbose(),
|
})
|
||||||
},
|
_ = setup.Close()
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.cancel()
|
p.cancel()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,13 +9,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Setup appends the read end of a pipe for setup params transmission and returns its fd.
|
// Setup appends the read end of a pipe for setup params transmission and returns its fd.
|
||||||
func Setup(extraFiles *[]*os.File) (int, *gob.Encoder, error) {
|
func Setup(extraFiles *[]*os.File) (int, *os.File, error) {
|
||||||
if r, w, err := os.Pipe(); err != nil {
|
if r, w, err := os.Pipe(); err != nil {
|
||||||
return -1, nil, err
|
return -1, nil, err
|
||||||
} else {
|
} else {
|
||||||
fd := 3 + len(*extraFiles)
|
fd := 3 + len(*extraFiles)
|
||||||
*extraFiles = append(*extraFiles, r)
|
*extraFiles = append(*extraFiles, r)
|
||||||
return fd, gob.NewEncoder(w), nil
|
return fd, w, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package container_test
|
package container_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/gob"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
@ -59,12 +60,16 @@ func TestSetupReceive(t *testing.T) {
|
|||||||
|
|
||||||
encoderDone := make(chan error, 1)
|
encoderDone := make(chan error, 1)
|
||||||
extraFiles := make([]*os.File, 0, 1)
|
extraFiles := make([]*os.File, 0, 1)
|
||||||
if fd, encoder, err := container.Setup(&extraFiles); err != nil {
|
deadline, _ := t.Deadline()
|
||||||
|
if fd, f, err := container.Setup(&extraFiles); err != nil {
|
||||||
t.Fatalf("Setup: error = %v", err)
|
t.Fatalf("Setup: error = %v", err)
|
||||||
} else if fd != 3 {
|
} else if fd != 3 {
|
||||||
t.Fatalf("Setup: fd = %d, want 3", fd)
|
t.Fatalf("Setup: fd = %d, want 3", fd)
|
||||||
} else {
|
} else {
|
||||||
go func() { encoderDone <- encoder.Encode(payload) }()
|
if err = f.SetDeadline(deadline); err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
go func() { encoderDone <- gob.NewEncoder(f).Encode(payload) }()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(extraFiles) != 1 {
|
if len(extraFiles) != 1 {
|
||||||
|
|||||||
@ -20,8 +20,12 @@ import (
|
|||||||
"hakurei.app/system"
|
"hakurei.app/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Duration to wait for shim to exit on top of container WaitDelay.
|
const (
|
||||||
const shimWaitTimeout = 5 * time.Second
|
// Duration to wait for shim to exit on top of container WaitDelay.
|
||||||
|
shimWaitTimeout = 5 * time.Second
|
||||||
|
// Timeout from setup pipe creation to when outcomeState is fully written.
|
||||||
|
shimSetupTimeout = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// mainState holds persistent state bound to outcome.main.
|
// mainState holds persistent state bound to outcome.main.
|
||||||
type mainState struct {
|
type mainState struct {
|
||||||
@ -214,7 +218,7 @@ func (k *outcome) main(msg message.Msg) {
|
|||||||
hsuPath := internal.MustHsuPath()
|
hsuPath := internal.MustHsuPath()
|
||||||
|
|
||||||
// ms.beforeExit required beyond this point
|
// ms.beforeExit required beyond this point
|
||||||
ms := &mainState{Msg: msg, k: k}
|
ms := mainState{Msg: msg, k: k}
|
||||||
|
|
||||||
if err := k.sys.Commit(); err != nil {
|
if err := k.sys.Commit(); err != nil {
|
||||||
ms.fatal("cannot commit system setup:", err)
|
ms.fatal("cannot commit system setup:", err)
|
||||||
@ -232,11 +236,12 @@ func (k *outcome) main(msg message.Msg) {
|
|||||||
// shim runs in the same session as monitor; see shim.go for behaviour
|
// shim runs in the same session as monitor; see shim.go for behaviour
|
||||||
ms.cmd.Cancel = func() error { return ms.cmd.Process.Signal(syscall.SIGCONT) }
|
ms.cmd.Cancel = func() error { return ms.cmd.Process.Signal(syscall.SIGCONT) }
|
||||||
|
|
||||||
var e *gob.Encoder
|
var shimPipe *os.File
|
||||||
if fd, encoder, err := container.Setup(&ms.cmd.ExtraFiles); err != nil {
|
if fd, w, err := container.Setup(&ms.cmd.ExtraFiles); err != nil {
|
||||||
ms.fatal("cannot create shim setup pipe:", err)
|
ms.fatal("cannot create shim setup pipe:", err)
|
||||||
|
panic("unreachable")
|
||||||
} else {
|
} else {
|
||||||
e = encoder
|
shimPipe = w
|
||||||
ms.cmd.Env = []string{
|
ms.cmd.Env = []string{
|
||||||
// passed through to shim by hsu
|
// passed through to shim by hsu
|
||||||
shimEnv + "=" + strconv.Itoa(fd),
|
shimEnv + "=" + strconv.Itoa(fd),
|
||||||
@ -262,23 +267,14 @@ func (k *outcome) main(msg message.Msg) {
|
|||||||
go func() { ms.cmdWait <- ms.cmd.Wait(); cancel() }()
|
go func() { ms.cmdWait <- ms.cmd.Wait(); cancel() }()
|
||||||
ms.Time = &startTime
|
ms.Time = &startTime
|
||||||
|
|
||||||
// unfortunately the I/O here cannot be directly canceled;
|
if err := shimPipe.SetDeadline(time.Now().Add(shimSetupTimeout)); err != nil {
|
||||||
// the cancellation path leads to fatal in this case so that is fine
|
msg.Verbose(err.Error())
|
||||||
select {
|
|
||||||
case err := <-func() (setupErr chan error) {
|
|
||||||
setupErr = make(chan error, 1)
|
|
||||||
go func() { setupErr <- e.Encode(k.state) }()
|
|
||||||
return
|
|
||||||
}():
|
|
||||||
if err != nil {
|
|
||||||
msg.Resume()
|
|
||||||
ms.fatal("cannot transmit shim config:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-ctx.Done():
|
|
||||||
msg.Resume()
|
|
||||||
ms.fatal("shim context canceled:", newWithMessageError("shim setup canceled", ctx.Err()))
|
|
||||||
}
|
}
|
||||||
|
if err := gob.NewEncoder(shimPipe).Encode(k.state); err != nil {
|
||||||
|
msg.Resume()
|
||||||
|
ms.fatal("cannot transmit shim config:", err)
|
||||||
|
}
|
||||||
|
_ = shimPipe.Close()
|
||||||
|
|
||||||
// shim accepted setup payload, create process state
|
// shim accepted setup payload, create process state
|
||||||
if ok, err := ms.store.Do(k.state.identity.unwrap(), func(c store.Cursor) {
|
if ok, err := ms.store.Do(k.state.identity.unwrap(), func(c store.Cursor) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user