container: initialise cmd early
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m29s
Test / Hakurei (push) Successful in 4m15s
Test / Sandbox (race detector) (push) Successful in 4m43s
Test / Hpkg (push) Successful in 4m48s
Test / Hakurei (race detector) (push) Successful in 6m9s
Test / Flake checks (push) Successful in 1m18s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m29s
Test / Hakurei (push) Successful in 4m15s
Test / Sandbox (race detector) (push) Successful in 4m43s
Test / Hpkg (push) Successful in 4m48s
Test / Hakurei (race detector) (push) Successful in 6m9s
Test / Flake checks (push) Successful in 1m18s
This allows use of more cmd methods. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
e271fa77aa
commit
3f25c3f0af
@ -64,7 +64,7 @@ type (
|
|||||||
Args []string
|
Args []string
|
||||||
// Deliver SIGINT to the initial process on context cancellation.
|
// Deliver SIGINT to the initial process on context cancellation.
|
||||||
ForwardCancel bool
|
ForwardCancel bool
|
||||||
// time to wait for linger processes after death of initial process
|
// Time to wait for processes lingering after the initial process terminates.
|
||||||
AdoptWaitDelay time.Duration
|
AdoptWaitDelay time.Duration
|
||||||
|
|
||||||
// Mapped Uid in user namespace.
|
// Mapped Uid in user namespace.
|
||||||
@ -152,15 +152,13 @@ func (e *StartError) Message() string {
|
|||||||
|
|
||||||
// Start starts the container init. The init process blocks until Serve is called.
|
// Start starts the container init. The init process blocks until Serve is called.
|
||||||
func (p *Container) Start() error {
|
func (p *Container) Start() error {
|
||||||
if p.cmd != nil {
|
if p == nil || p.cmd == nil ||
|
||||||
|
p.Ops == nil || len(*p.Ops) == 0 {
|
||||||
|
return errors.New("container: starting an invalid container")
|
||||||
|
}
|
||||||
|
if p.cmd.Process != nil {
|
||||||
return errors.New("container: already started")
|
return errors.New("container: already started")
|
||||||
}
|
}
|
||||||
if p.Ops == nil || len(*p.Ops) == 0 {
|
|
||||||
return errors.New("container: starting an empty container")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(p.ctx)
|
|
||||||
p.cancel = cancel
|
|
||||||
|
|
||||||
// map to overflow id to work around ownership checks
|
// map to overflow id to work around ownership checks
|
||||||
if p.Uid < 1 {
|
if p.Uid < 1 {
|
||||||
@ -182,9 +180,17 @@ func (p *Container) Start() error {
|
|||||||
p.AdoptWaitDelay = 0
|
p.AdoptWaitDelay = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
p.cmd = exec.CommandContext(ctx, MustExecutable())
|
if p.cmd.Stdin == nil {
|
||||||
|
p.cmd.Stdin = p.Stdin
|
||||||
|
}
|
||||||
|
if p.cmd.Stdout == nil {
|
||||||
|
p.cmd.Stdout = p.Stdout
|
||||||
|
}
|
||||||
|
if p.cmd.Stderr == nil {
|
||||||
|
p.cmd.Stderr = p.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
p.cmd.Args = []string{initName}
|
p.cmd.Args = []string{initName}
|
||||||
p.cmd.Stdin, p.cmd.Stdout, p.cmd.Stderr = p.Stdin, p.Stdout, p.Stderr
|
|
||||||
p.cmd.WaitDelay = p.WaitDelay
|
p.cmd.WaitDelay = p.WaitDelay
|
||||||
if p.Cancel != nil {
|
if p.Cancel != nil {
|
||||||
p.cmd.Cancel = func() error { return p.Cancel(p.cmd) }
|
p.cmd.Cancel = func() error { return p.Cancel(p.cmd) }
|
||||||
@ -330,7 +336,7 @@ func (p *Container) Serve() error {
|
|||||||
|
|
||||||
// Wait waits for the container init process to exit and releases any resources associated with the [Container].
|
// Wait waits for the container init process to exit and releases any resources associated with the [Container].
|
||||||
func (p *Container) Wait() error {
|
func (p *Container) Wait() error {
|
||||||
if p.cmd == nil {
|
if p.cmd == nil || p.cmd.Process == nil {
|
||||||
return EINVAL
|
return EINVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +348,36 @@ func (p *Container) Wait() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StdinPipe calls the [exec.Cmd] method with the same name.
|
||||||
|
func (p *Container) StdinPipe() (w io.WriteCloser, err error) {
|
||||||
|
if p.Stdin != nil {
|
||||||
|
return nil, errors.New("container: Stdin already set")
|
||||||
|
}
|
||||||
|
w, err = p.cmd.StdinPipe()
|
||||||
|
p.Stdin = p.cmd.Stdin
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdoutPipe calls the [exec.Cmd] method with the same name.
|
||||||
|
func (p *Container) StdoutPipe() (r io.ReadCloser, err error) {
|
||||||
|
if p.Stdout != nil {
|
||||||
|
return nil, errors.New("container: Stdout already set")
|
||||||
|
}
|
||||||
|
r, err = p.cmd.StdoutPipe()
|
||||||
|
p.Stdout = p.cmd.Stdout
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StderrPipe calls the [exec.Cmd] method with the same name.
|
||||||
|
func (p *Container) StderrPipe() (r io.ReadCloser, err error) {
|
||||||
|
if p.Stderr != nil {
|
||||||
|
return nil, errors.New("container: Stderr already set")
|
||||||
|
}
|
||||||
|
r, err = p.cmd.StderrPipe()
|
||||||
|
p.Stderr = p.cmd.Stderr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Container) String() string {
|
func (p *Container) String() string {
|
||||||
return fmt.Sprintf("argv: %q, filter: %v, rules: %d, flags: %#x, presets: %#x",
|
return fmt.Sprintf("argv: %q, filter: %v, rules: %d, flags: %#x, presets: %#x",
|
||||||
p.Args, !p.SeccompDisable, len(p.SeccompRules), int(p.SeccompFlags), int(p.SeccompPresets))
|
p.Args, !p.SeccompDisable, len(p.SeccompRules), int(p.SeccompFlags), int(p.SeccompPresets))
|
||||||
@ -357,7 +393,11 @@ func (p *Container) ProcessState() *os.ProcessState {
|
|||||||
|
|
||||||
// New returns the address to a new instance of [Container] that requires further initialisation before use.
|
// New returns the address to a new instance of [Container] that requires further initialisation before use.
|
||||||
func New(ctx context.Context) *Container {
|
func New(ctx context.Context) *Container {
|
||||||
return &Container{ctx: ctx, Params: Params{Ops: new(Ops)}}
|
p := &Container{ctx: ctx, Params: Params{Ops: new(Ops)}}
|
||||||
|
c, cancel := context.WithCancel(ctx)
|
||||||
|
p.cancel = cancel
|
||||||
|
p.cmd = exec.CommandContext(c, MustExecutable())
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields.
|
// NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields.
|
||||||
|
@ -11,10 +11,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestContainer(t *testing.T) {
|
func TestContainer(t *testing.T) {
|
||||||
t.Run("start empty container", func(t *testing.T) {
|
t.Run("start invalid container", func(t *testing.T) {
|
||||||
h := helper.New(t.Context(), container.MustAbs(container.Nonexistent), "hakurei", argsWt, false, argF, nil, nil)
|
h := helper.New(t.Context(), container.MustAbs(container.Nonexistent), "hakurei", argsWt, false, argF, nil, nil)
|
||||||
|
|
||||||
wantErr := "container: starting an empty container"
|
wantErr := "container: starting an invalid container"
|
||||||
if err := h.Start(); err == nil || err.Error() != wantErr {
|
if err := h.Start(); err == nil || err.Error() != wantErr {
|
||||||
t.Errorf("Start: error = %v, wantErr %q",
|
t.Errorf("Start: error = %v, wantErr %q",
|
||||||
err, wantErr)
|
err, wantErr)
|
||||||
|
@ -6,8 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -47,6 +49,10 @@ func argFChecked(argsFd, statFd int) (args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
containerTimeout = 30 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// this function tests an implementation of the helper.Helper interface
|
// this function tests an implementation of the helper.Helper interface
|
||||||
func testHelper(t *testing.T, createHelper func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper) {
|
func testHelper(t *testing.T, createHelper func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper) {
|
||||||
oldWaitDelay := helper.WaitDelay
|
oldWaitDelay := helper.WaitDelay
|
||||||
@ -54,18 +60,15 @@ func testHelper(t *testing.T, createHelper func(ctx context.Context, setOutput f
|
|||||||
t.Cleanup(func() { helper.WaitDelay = oldWaitDelay })
|
t.Cleanup(func() { helper.WaitDelay = oldWaitDelay })
|
||||||
|
|
||||||
t.Run("start helper with status channel and wait", func(t *testing.T) {
|
t.Run("start helper with status channel and wait", func(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(t.Context(), containerTimeout)
|
||||||
stdout := new(strings.Builder)
|
stdout := new(strings.Builder)
|
||||||
h := createHelper(ctx, func(stdoutP, stderrP *io.Writer) { *stdoutP, *stderrP = stdout, os.Stderr }, true)
|
h := createHelper(ctx, func(stdoutP, stderrP *io.Writer) { *stdoutP, *stderrP = stdout, os.Stderr }, true)
|
||||||
|
|
||||||
t.Run("wait not yet started helper", func(t *testing.T) {
|
t.Run("wait not yet started helper", func(t *testing.T) {
|
||||||
defer func() {
|
if err := h.Wait(); !reflect.DeepEqual(err, syscall.EINVAL) &&
|
||||||
r := recover()
|
!reflect.DeepEqual(err, errors.New("exec: not started")) {
|
||||||
if r == nil {
|
t.Errorf("Wait: error = %v", err)
|
||||||
t.Fatalf("Wait did not panic")
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
panic(fmt.Sprintf("unreachable: %v", h.Wait()))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Log("starting helper stub")
|
t.Log("starting helper stub")
|
||||||
@ -108,7 +111,7 @@ func testHelper(t *testing.T, createHelper func(ctx context.Context, setOutput f
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("start helper and wait", func(t *testing.T) {
|
t.Run("start helper and wait", func(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(t.Context(), containerTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
stdout := new(strings.Builder)
|
stdout := new(strings.Builder)
|
||||||
h := createHelper(ctx, func(stdoutP, stderrP *io.Writer) { *stdoutP, *stderrP = stdout, os.Stderr }, false)
|
h := createHelper(ctx, func(stdoutP, stderrP *io.Writer) { *stdoutP, *stderrP = stdout, os.Stderr }, false)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user