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