oci: wait for runtime to write pidfile before starting timer
@@ -346,8 +346,13 @@ func (r *runtimeOCI) ExecSyncContainer(ctx context.Context, c *Container, comman
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
- err = cmd.Start()
+ pidFileCreatedCh, err := WatchForFile(pidFile, notify.InModify, notify.InMovedTo)
if err != nil {
+ return nil, errors.Wrapf(err, "failed to watch %s", pidFile)
+ }
+
+ doneErr := cmd.Start()
+ if doneErr != nil {
return nil, err
}
@@ -357,7 +362,23 @@ func (r *runtimeOCI) ExecSyncContainer(ctx context.Context, c *Container, comman
done <- cmd.Wait()
}()
- if timeout > 0 {
+ // First, wait for the pid file to be created.
+ // When it is, the timer begins for the exec process.
+ // If the command fails before that happens, however,
+ // that needs to be caught.
+ select {
+ case <-pidFileCreatedCh:
+ case doneErr = <-done:
+ close(done)
+ }
+
+ switch {
+ case doneErr != nil:
+ // If we've already gotten an error from done
+ // the runtime finished before writing the pid file
+ // (probably because the command didn't exist).
+ case timeout > 0:
+ // If there's a timeout, wait for that timeout duration.
select {
case <-time.After(time.Second * time.Duration(timeout)):
// Ensure the process is not left behind
@@ -373,17 +394,18 @@ func (r *runtimeOCI) ExecSyncContainer(ctx context.Context, c *Container, comman
Stderr: []byte(conmonconfig.TimedOutMessage),
ExitCode: -1,
}, nil
- case err = <-done:
+ case doneErr = <-done:
break
}
- } else {
- err = <-done
+ default:
+ // If no timeout, just wait until the command finishes.
+ doneErr = <-done
}
// gather exit code from err
exitCode := int32(0)
- if err != nil {
- if exitError, ok := err.(*exec.ExitError); ok {
+ if doneErr != nil {
+ if exitError, ok := doneErr.(*exec.ExitError); ok {
exitCode = int32(exitError.ExitCode())
}
}
