Add create/start to exec processes in shim

This splits up the create and start of an exec process in the shim to
have two separate steps like the initial process.  This will allow
better state reporting for individual process along with a more robust
wait for execs.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby
2017-07-31 11:26:21 -04:00
parent 2533bfeaaf
commit 63878d14ea
11 changed files with 749 additions and 249 deletions

View File

@@ -36,6 +36,8 @@ type execProcess struct {
closers []io.Closer
stdin io.Closer
stdio stdio
path string
spec specs.Process
parent *initProcess
}
@@ -44,43 +46,6 @@ func newExecProcess(context context.Context, path string, r *shimapi.ExecProcess
if err := identifiers.Validate(id); err != nil {
return nil, errors.Wrapf(err, "invalid exec id")
}
e := &execProcess{
id: id,
parent: parent,
stdio: stdio{
stdin: r.Stdin,
stdout: r.Stdout,
stderr: r.Stderr,
terminal: r.Terminal,
},
}
var (
err error
socket *runc.Socket
io runc.IO
pidfile = filepath.Join(path, fmt.Sprintf("%s.pid", id))
)
if r.Terminal {
if socket, err = runc.NewConsoleSocket(filepath.Join(path, "pty.sock")); err != nil {
return nil, errors.Wrap(err, "failed to create runc console socket")
}
defer os.Remove(socket.Path())
} else {
// TODO: get uid/gid
if io, err = runc.NewPipeIO(0, 0); err != nil {
return nil, errors.Wrap(err, "failed to create runc io pipes")
}
e.io = io
}
opts := &runc.ExecOpts{
PidFile: pidfile,
IO: io,
Detach: true,
}
if socket != nil {
opts.ConsoleSocket = socket
}
// process exec request
var spec specs.Process
if err := json.Unmarshal(r.Spec.Value, &spec); err != nil {
@@ -88,39 +53,18 @@ func newExecProcess(context context.Context, path string, r *shimapi.ExecProcess
}
spec.Terminal = r.Terminal
if err := parent.runtime.Exec(context, parent.id, spec, opts); err != nil {
return nil, parent.runtimeError(err, "OCI runtime exec failed")
e := &execProcess{
id: id,
path: path,
parent: parent,
spec: spec,
stdio: stdio{
stdin: r.Stdin,
stdout: r.Stdout,
stderr: r.Stderr,
terminal: r.Terminal,
},
}
if r.Stdin != "" {
sc, err := fifo.OpenFifo(context, r.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
if err != nil {
return nil, errors.Wrapf(err, "failed to open stdin fifo %s", r.Stdin)
}
e.closers = append(e.closers, sc)
e.stdin = sc
}
var copyWaitGroup sync.WaitGroup
if socket != nil {
console, err := socket.ReceiveMaster()
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve console master")
}
console, err = e.parent.platform.copyConsole(context, console, r.Stdin, r.Stdout, r.Stderr, &e.WaitGroup, &copyWaitGroup)
if err != nil {
return nil, errors.Wrap(err, "failed to start console copy")
}
e.console = console
} else {
if err := copyPipes(context, io, r.Stdin, r.Stdout, r.Stderr, &e.WaitGroup, &copyWaitGroup); err != nil {
return nil, errors.Wrap(err, "failed to start io pipe copy")
}
}
copyWaitGroup.Wait()
pid, err := runc.ReadPidFile(opts.PidFile)
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve OCI runtime exec pid")
}
e.pid = pid
return e, nil
}
@@ -178,3 +122,63 @@ func (e *execProcess) Stdin() io.Closer {
func (e *execProcess) Stdio() stdio {
return e.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 {
if socket, err = runc.NewConsoleSocket(filepath.Join(e.path, "pty.sock")); err != nil {
return errors.Wrap(err, "failed to create runc console socket")
}
defer os.Remove(socket.Path())
} else {
if io, err = runc.NewPipeIO(0, 0); err != nil {
return errors.Wrap(err, "failed to create runc io pipes")
}
e.io = io
}
opts := &runc.ExecOpts{
PidFile: pidfile,
IO: io,
Detach: true,
}
if socket != nil {
opts.ConsoleSocket = socket
}
if err := e.parent.runtime.Exec(ctx, e.parent.id, e.spec, opts); err != nil {
return e.parent.runtimeError(err, "OCI runtime exec failed")
}
if e.stdio.stdin != "" {
sc, err := fifo.OpenFifo(ctx, e.stdio.stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
if err != nil {
return errors.Wrapf(err, "failed to open stdin fifo %s", e.stdio.stdin)
}
e.closers = append(e.closers, sc)
e.stdin = sc
}
var copyWaitGroup sync.WaitGroup
if socket != nil {
console, err := socket.ReceiveMaster()
if err != nil {
return errors.Wrap(err, "failed to retrieve console master")
}
e.console = console
if err := e.parent.platform.copyConsole(ctx, console, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.WaitGroup, &copyWaitGroup); 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, &copyWaitGroup); err != nil {
return errors.Wrap(err, "failed to start io pipe copy")
}
}
copyWaitGroup.Wait()
pid, err := runc.ReadPidFile(opts.PidFile)
if err != nil {
return errors.Wrap(err, "failed to retrieve OCI runtime exec pid")
}
e.pid = pid
return nil
}