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 { if err != nil {
return nil, err 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) return copyIO(fifos, streams)
} }
} }

View File

@ -23,6 +23,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os/exec" "os/exec"
"runtime" "runtime"
"strings" "strings"
@ -1481,3 +1482,52 @@ func TestBindLowPortNonOpt(t *testing.T) {
t.Fatal(err) 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") return errors.Wrap(err, "creating new NULL IO")
} }
} else { } 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") 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") return errors.Wrap(err, "creating new NULL IO")
} }
} else { } 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") 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) 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) i.dest(fw, fr)
} }
if stdin == "" { if stdin == "" {
rio.Stdin().Close()
return nil return nil
} }
f, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) 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/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40

View File

@ -34,6 +34,24 @@ type StartCloser interface {
CloseAfterStart() error 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) { func newPipe() (*pipe, error) {
r, w, err := os.Pipe() r, w, err := os.Pipe()
if err != nil { if err != nil {
@ -65,14 +83,23 @@ type pipeIO struct {
} }
func (i *pipeIO) Stdin() io.WriteCloser { func (i *pipeIO) Stdin() io.WriteCloser {
if i.in == nil {
return nil
}
return i.in.w return i.in.w
} }
func (i *pipeIO) Stdout() io.ReadCloser { func (i *pipeIO) Stdout() io.ReadCloser {
if i.out == nil {
return nil
}
return i.out.r return i.out.r
} }
func (i *pipeIO) Stderr() io.ReadCloser { func (i *pipeIO) Stderr() io.ReadCloser {
if i.err == nil {
return nil
}
return i.err.r return i.err.r
} }
@ -83,28 +110,38 @@ func (i *pipeIO) Close() error {
i.out, i.out,
i.err, i.err,
} { } {
if cerr := v.Close(); err == nil { if v != nil {
err = cerr if cerr := v.Close(); err == nil {
err = cerr
}
} }
} }
return err return err
} }
func (i *pipeIO) CloseAfterStart() error { func (i *pipeIO) CloseAfterStart() error {
for _, f := range []*os.File{ for _, f := range []*pipe{
i.out.w, i.out,
i.err.w, i.err,
} { } {
f.Close() if f != nil {
f.w.Close()
}
} }
return nil return nil
} }
// Set sets the io to the exec.Cmd // Set sets the io to the exec.Cmd
func (i *pipeIO) Set(cmd *exec.Cmd) { func (i *pipeIO) Set(cmd *exec.Cmd) {
cmd.Stdin = i.in.r if i.in != nil {
cmd.Stdout = i.out.w cmd.Stdin = i.in.r
cmd.Stderr = i.err.w }
if i.out != nil {
cmd.Stdout = i.out.w
}
if i.err != nil {
cmd.Stderr = i.err.w
}
} }
func NewSTDIO() (IO, error) { func NewSTDIO() (IO, error) {

View File

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

View File

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

View File

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