Merge pull request #2618 from crosbymichael/no-stdin

Don't provide IO when it's not set
This commit is contained in:
Michael Crosby 2018-09-10 11:26:49 -04:00 committed by GitHub
commit 12c877f57a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 174 additions and 58 deletions

View File

@ -141,6 +141,15 @@ func NewCreator(opts ...Opt) Creator {
if err != nil {
return nil, err
}
if streams.Stdin == nil {
fifos.Stdin = ""
}
if streams.Stdout == nil {
fifos.Stdout = ""
}
if streams.Stderr == nil {
fifos.Stderr = ""
}
return copyIO(fifos, streams)
}
}

View File

@ -23,6 +23,7 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"os/exec"
"runtime"
"strings"
@ -1481,3 +1482,52 @@ func TestBindLowPortNonOpt(t *testing.T) {
t.Fatal(err)
}
}
func TestContainerNoSTDIN(t *testing.T) {
t.Parallel()
client, err := newClient(t, address)
if err != nil {
t.Fatal(err)
}
defer client.Close()
var (
image Image
ctx, cancel = testContext()
id = t.Name()
)
defer cancel()
image, err = client.GetImage(ctx, testImage)
if err != nil {
t.Fatal(err)
}
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withExitStatus(0)), WithNewSnapshot(id, image))
if err != nil {
t.Fatal(err)
}
defer container.Delete(ctx, WithSnapshotCleanup)
task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStreams(nil, ioutil.Discard, ioutil.Discard)))
if err != nil {
t.Fatal(err)
}
defer task.Delete(ctx)
statusC, err := task.Wait(ctx)
if err != nil {
t.Fatal(err)
}
if err := task.Start(ctx); err != nil {
t.Fatal(err)
}
status := <-statusC
code, _, err := status.Result()
if err != nil {
t.Fatal(err)
}
if code != 0 {
t.Errorf("expected status 0 from wait but received %d", code)
}
}

View File

@ -147,7 +147,7 @@ func (e *execProcess) start(ctx context.Context) (err error) {
return errors.Wrap(err, "creating new NULL IO")
}
} else {
if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID); err != nil {
if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil {
return errors.Wrap(err, "failed to create runc io pipes")
}
}

View File

@ -123,7 +123,7 @@ func (p *Init) Create(ctx context.Context, r *CreateConfig) error {
return errors.Wrap(err, "creating new NULL IO")
}
} else {
if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID); err != nil {
if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID, withConditionalIO(p.stdio)); err != nil {
return errors.Wrap(err, "failed to create OCI runtime io pipes")
}
}
@ -399,3 +399,11 @@ func (p *Init) runtimeError(rErr error, msg string) error {
return errors.Errorf("%s: %s", msg, rMsg)
}
}
func withConditionalIO(c proc.Stdio) runc.IOOpt {
return func(o *runc.IOOption) {
o.OpenStdin = c.Stdin != ""
o.OpenStdout = c.Stdout != ""
o.OpenStderr = c.Stderr != ""
}
}

View File

@ -109,7 +109,6 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w
i.dest(fw, fr)
}
if stdin == "" {
rio.Stdin().Close()
return nil
}
f, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)

View File

@ -1,4 +1,4 @@
github.com/containerd/go-runc acb7c88cac264acca9b5eae187a117f4d77a1292
github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40

View File

@ -34,6 +34,24 @@ type StartCloser interface {
CloseAfterStart() error
}
// IOOpt sets I/O creation options
type IOOpt func(*IOOption)
// IOOption holds I/O creation options
type IOOption struct {
OpenStdin bool
OpenStdout bool
OpenStderr bool
}
func defaultIOOption() *IOOption {
return &IOOption{
OpenStdin: true,
OpenStdout: true,
OpenStderr: true,
}
}
func newPipe() (*pipe, error) {
r, w, err := os.Pipe()
if err != nil {
@ -65,14 +83,23 @@ type pipeIO struct {
}
func (i *pipeIO) Stdin() io.WriteCloser {
if i.in == nil {
return nil
}
return i.in.w
}
func (i *pipeIO) Stdout() io.ReadCloser {
if i.out == nil {
return nil
}
return i.out.r
}
func (i *pipeIO) Stderr() io.ReadCloser {
if i.err == nil {
return nil
}
return i.err.r
}
@ -83,28 +110,38 @@ func (i *pipeIO) Close() error {
i.out,
i.err,
} {
if cerr := v.Close(); err == nil {
err = cerr
if v != nil {
if cerr := v.Close(); err == nil {
err = cerr
}
}
}
return err
}
func (i *pipeIO) CloseAfterStart() error {
for _, f := range []*os.File{
i.out.w,
i.err.w,
for _, f := range []*pipe{
i.out,
i.err,
} {
f.Close()
if f != nil {
f.w.Close()
}
}
return nil
}
// Set sets the io to the exec.Cmd
func (i *pipeIO) Set(cmd *exec.Cmd) {
cmd.Stdin = i.in.r
cmd.Stdout = i.out.w
cmd.Stderr = i.err.w
if i.in != nil {
cmd.Stdin = i.in.r
}
if i.out != nil {
cmd.Stdout = i.out.w
}
if i.err != nil {
cmd.Stderr = i.err.w
}
}
func NewSTDIO() (IO, error) {

View File

@ -24,8 +24,15 @@ import (
)
// NewPipeIO creates pipe pairs to be used with runc
func NewPipeIO(uid, gid int) (i IO, err error) {
var pipes []*pipe
func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
option := defaultIOOption()
for _, o := range opts {
o(option)
}
var (
pipes []*pipe
stdin, stdout, stderr *pipe
)
// cleanup in case of an error
defer func() {
if err != nil {
@ -34,33 +41,33 @@ func NewPipeIO(uid, gid int) (i IO, err error) {
}
}
}()
stdin, err := newPipe()
if err != nil {
return nil, err
if option.OpenStdin {
if stdin, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdin)
if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil {
return nil, errors.Wrap(err, "failed to chown stdin")
}
}
pipes = append(pipes, stdin)
if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil {
return nil, errors.Wrap(err, "failed to chown stdin")
if option.OpenStdout {
if stdout, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdout)
if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil {
return nil, errors.Wrap(err, "failed to chown stdout")
}
}
stdout, err := newPipe()
if err != nil {
return nil, err
if option.OpenStderr {
if stderr, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stderr)
if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil {
return nil, errors.Wrap(err, "failed to chown stderr")
}
}
pipes = append(pipes, stdout)
if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil {
return nil, errors.Wrap(err, "failed to chown stdout")
}
stderr, err := newPipe()
if err != nil {
return nil, err
}
pipes = append(pipes, stderr)
if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil {
return nil, errors.Wrap(err, "failed to chown stderr")
}
return &pipeIO{
in: stdin,
out: stdout,

View File

@ -19,8 +19,15 @@
package runc
// NewPipeIO creates pipe pairs to be used with runc
func NewPipeIO() (i IO, err error) {
var pipes []*pipe
func NewPipeIO(opts ...IOOpt) (i IO, err error) {
option := defaultIOOption()
for _, o := range opts {
o(option)
}
var (
pipes []*pipe
stdin, stdout, stderr *pipe
)
// cleanup in case of an error
defer func() {
if err != nil {
@ -29,24 +36,24 @@ func NewPipeIO() (i IO, err error) {
}
}
}()
stdin, err := newPipe()
if err != nil {
return nil, err
if option.OpenStdin {
if stdin, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdin)
}
pipes = append(pipes, stdin)
stdout, err := newPipe()
if err != nil {
return nil, err
if option.OpenStdout {
if stdout, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdout)
}
pipes = append(pipes, stdout)
stderr, err := newPipe()
if err != nil {
return nil, err
if option.OpenStderr {
if stderr, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stderr)
}
pipes = append(pipes, stderr)
return &pipeIO{
in: stdin,
out: stdout,

View File

@ -608,9 +608,8 @@ func parseVersion(data []byte) (Version, error) {
var v Version
parts := strings.Split(strings.TrimSpace(string(data)), "\n")
if len(parts) != 3 {
return v, ErrParseRuncVersion
return v, nil
}
for i, p := range []struct {
dest *string
split string