From eb58ecab7ccfab813865ae3a177385d13b1e37ed Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 11 Aug 2017 16:40:08 -0400 Subject: [PATCH] Add null io option This adds null IO option for efficient handling of IO. It provides a container directly with `/dev/null` and does not require any io.Copy within the shim whenever a user does not want the IO of the container. Signed-off-by: Michael Crosby --- cmd/stress/main.go | 4 +--- container_test.go | 3 +-- io.go | 5 +++++ linux/shim/exec.go | 14 ++++++++------ linux/shim/init.go | 20 +++++++++++++------- linux/shim/process.go | 4 ++++ 6 files changed, 32 insertions(+), 18 deletions(-) diff --git a/cmd/stress/main.go b/cmd/stress/main.go index c24659a6f..977ea66ed 100644 --- a/cmd/stress/main.go +++ b/cmd/stress/main.go @@ -1,10 +1,8 @@ package main import ( - "bytes" "context" "fmt" - "io/ioutil" "os" "os/signal" "runtime" @@ -205,7 +203,7 @@ func (w *worker) runContainer(ctx context.Context, id string) error { } defer c.Delete(ctx, containerd.WithSnapshotCleanup) - task, err := c.NewTask(ctx, containerd.NewIO(bytes.NewBuffer(nil), ioutil.Discard, ioutil.Discard)) + task, err := c.NewTask(ctx, containerd.NullIO) if err != nil { return err } diff --git a/container_test.go b/container_test.go index e9ecb9e2f..2010d1069 100644 --- a/container_test.go +++ b/container_test.go @@ -19,8 +19,7 @@ import ( ) func empty() IOCreation { - null := ioutil.Discard - return NewIO(bytes.NewBuffer(nil), null, null) + return NullIO } func TestContainerList(t *testing.T) { diff --git a/io.go b/io.go index 814ff5399..e2d52f7d1 100644 --- a/io.go +++ b/io.go @@ -126,6 +126,11 @@ func StdioTerminal(id string) (*IO, error) { return NewIOWithTerminal(os.Stdin, os.Stdout, os.Stderr, true)(id) } +// NullIO redirects the container's IO into /dev/null +func NullIO(id string) (*IO, error) { + return &IO{}, nil +} + // FIFOSet is a set of fifos for use with tasks type FIFOSet struct { // Dir is the directory holding the task fifos diff --git a/linux/shim/exec.go b/linux/shim/exec.go index a5fb24bf2..75d4a3221 100644 --- a/linux/shim/exec.go +++ b/linux/shim/exec.go @@ -133,7 +133,6 @@ func (e *execProcess) Stdio() stdio { func (e *execProcess) Start(ctx context.Context) (err error) { var ( socket *runc.Socket - io runc.IO pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)) ) if e.stdio.terminal { @@ -141,15 +140,18 @@ func (e *execProcess) Start(ctx context.Context) (err error) { return errors.Wrap(err, "failed to create runc console socket") } defer socket.Close() + } else if e.stdio.isNull() { + if e.io, err = runc.NewNullIO(); err != nil { + return errors.Wrap(err, "creating new NULL IO") + } } else { - if io, err = runc.NewPipeIO(); err != nil { + if e.io, err = runc.NewPipeIO(); err != nil { return errors.Wrap(err, "failed to create runc io pipes") } - e.io = io } opts := &runc.ExecOpts{ PidFile: pidfile, - IO: io, + IO: e.io, Detach: true, } if socket != nil { @@ -175,8 +177,8 @@ func (e *execProcess) Start(ctx context.Context) (err error) { if e.console, err = e.parent.platform.copyConsole(ctx, console, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.WaitGroup, ©WaitGroup); err != nil { return errors.Wrap(err, "failed to start console copy") } - } else { - if err := copyPipes(ctx, io, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.WaitGroup, ©WaitGroup); err != nil { + } else if !e.stdio.isNull() { + if err := copyPipes(ctx, e.io, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.WaitGroup, ©WaitGroup); err != nil { return errors.Wrap(err, "failed to start io pipe copy") } } diff --git a/linux/shim/init.go b/linux/shim/init.go index c9bcf0b2a..b52cfe02f 100644 --- a/linux/shim/init.go +++ b/linux/shim/init.go @@ -117,18 +117,20 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor var ( err error socket *runc.Socket - io runc.IO ) if r.Terminal { if socket, err = runc.NewTempConsoleSocket(); err != nil { return nil, errors.Wrap(err, "failed to create OCI runtime console socket") } defer socket.Close() + } else if hasNoIO(r) { + if p.io, err = runc.NewNullIO(); err != nil { + return nil, errors.Wrap(err, "creating new NULL IO") + } } else { - if io, err = runc.NewPipeIO(); err != nil { + if p.io, err = runc.NewPipeIO(); err != nil { return nil, errors.Wrap(err, "failed to create OCI runtime io pipes") } - p.io = io } pidFile := filepath.Join(path, "init.pid") if r.Checkpoint != "" { @@ -139,7 +141,7 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor ParentPath: r.ParentCheckpoint, }, PidFile: pidFile, - IO: io, + IO: p.io, NoPivot: options.NoPivotRoot, Detach: true, NoSubreaper: true, @@ -150,7 +152,7 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor } else { opts := &runc.CreateOpts{ PidFile: pidFile, - IO: io, + IO: p.io, NoPivot: options.NoPivotRoot, NoNewKeyring: options.NoNewKeyring, } @@ -180,8 +182,8 @@ func newInitProcess(context context.Context, plat platform, path, namespace, wor return nil, errors.Wrap(err, "failed to start console copy") } p.console = console - } else { - if err := copyPipes(context, io, r.Stdin, r.Stdout, r.Stderr, &p.WaitGroup, ©WaitGroup); err != nil { + } else if !hasNoIO(r) { + if err := copyPipes(context, p.io, r.Stdin, r.Stdout, r.Stderr, &p.WaitGroup, ©WaitGroup); err != nil { return nil, errors.Wrap(err, "failed to start io pipe copy") } } @@ -427,3 +429,7 @@ func checkKillError(err error) error { } return errors.Wrapf(err, "unknown error after kill") } + +func hasNoIO(r *shimapi.CreateTaskRequest) bool { + return r.Stdin == "" && r.Stdout == "" && r.Stderr == "" +} diff --git a/linux/shim/process.go b/linux/shim/process.go index f005d8bb7..46f1b31c0 100644 --- a/linux/shim/process.go +++ b/linux/shim/process.go @@ -17,6 +17,10 @@ type stdio struct { terminal bool } +func (s stdio) isNull() bool { + return s.stdin == "" && s.stdout == "" && s.stderr == "" +} + type process interface { // ID returns the id for the process ID() string