Merge pull request #135 from docker/deps-errors
Return errors from shim and missing shim/runc
This commit is contained in:
commit
5617360b3a
3 changed files with 66 additions and 38 deletions
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
@ -12,14 +13,6 @@ import (
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupLogger() {
|
|
||||||
f, err := os.OpenFile("/tmp/shim.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
logrus.SetOutput(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// containerd-shim is a small shim that sits in front of a runtime implementation
|
// containerd-shim is a small shim that sits in front of a runtime implementation
|
||||||
// that allows it to be repartented to init and handle reattach from the caller.
|
// that allows it to be repartented to init and handle reattach from the caller.
|
||||||
//
|
//
|
||||||
|
@ -27,50 +20,65 @@ func setupLogger() {
|
||||||
// to the state directory where the shim can locate fifos and other information.
|
// to the state directory where the shim can locate fifos and other information.
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(filepath.Join(cwd, "shim-log.json"), os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
logrus.SetOutput(f)
|
||||||
|
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||||
|
if err := start(); err != nil {
|
||||||
|
// log the error instead of writing to stderr because the shim will have
|
||||||
|
// /dev/null as it's stdio because it is supposed to be reparented to system
|
||||||
|
// init and will not have anyone to read from it
|
||||||
|
logrus.Error(err)
|
||||||
|
f.Close()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func start() error {
|
||||||
// start handling signals as soon as possible so that things are properly reaped
|
// start handling signals as soon as possible so that things are properly reaped
|
||||||
// or if runtime exits before we hit the handler
|
// or if runtime exits before we hit the handler
|
||||||
signals := make(chan os.Signal, 2048)
|
signals := make(chan os.Signal, 2048)
|
||||||
signal.Notify(signals)
|
signal.Notify(signals)
|
||||||
// set the shim as the subreaper for all orphaned processes created by the container
|
// set the shim as the subreaper for all orphaned processes created by the container
|
||||||
if err := osutils.SetSubreaper(1); err != nil {
|
if err := osutils.SetSubreaper(1); err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: set as subreaper")
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// open the exit pipe
|
// open the exit pipe
|
||||||
f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
|
f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: open exit pipe")
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
control, err := os.OpenFile("control", syscall.O_RDWR, 0)
|
control, err := os.OpenFile("control", syscall.O_RDWR, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: open control pipe")
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer control.Close()
|
defer control.Close()
|
||||||
p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))
|
p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: create new process")
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := p.Close(); err != nil {
|
if err := p.Close(); err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: close stdio")
|
logrus.Warn(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := p.start(); err != nil {
|
if err := p.start(); err != nil {
|
||||||
p.delete()
|
p.delete()
|
||||||
logrus.WithField("error", err).Error("shim: start process")
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
var msg, w, h int
|
var msg, w, h int
|
||||||
if _, err := fmt.Fscanf(control, "%d %d %d\n", &msg, &w, &h); err != nil {
|
if _, err := fmt.Fscanf(control, "%d %d %d\n", &msg, &w, &h); err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: reading from control")
|
logrus.Warn(err)
|
||||||
}
|
}
|
||||||
logrus.Info("got control message")
|
|
||||||
switch msg {
|
switch msg {
|
||||||
case 0:
|
case 0:
|
||||||
// close stdin
|
// close stdin
|
||||||
|
@ -89,12 +97,11 @@ func main() {
|
||||||
}()
|
}()
|
||||||
var exitShim bool
|
var exitShim bool
|
||||||
for s := range signals {
|
for s := range signals {
|
||||||
logrus.WithField("signal", s).Debug("shim: received signal")
|
|
||||||
switch s {
|
switch s {
|
||||||
case syscall.SIGCHLD:
|
case syscall.SIGCHLD:
|
||||||
exits, err := osutils.Reap()
|
exits, err := osutils.Reap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithField("error", err).Error("shim: reaping child processes")
|
logrus.Warn(err)
|
||||||
}
|
}
|
||||||
for _, e := range exits {
|
for _, e := range exits {
|
||||||
// check to see if runtime is one of the processes that has exited
|
// check to see if runtime is one of the processes that has exited
|
||||||
|
@ -105,10 +112,7 @@ func main() {
|
||||||
"status": e.Status,
|
"status": e.Status,
|
||||||
}).Info("shim: runtime exited")
|
}).Info("shim: runtime exited")
|
||||||
if err := writeInt("exitStatus", e.Status); err != nil {
|
if err := writeInt("exitStatus", e.Status); err != nil {
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{"status": e.Status}).Warn(err)
|
||||||
"error": err,
|
|
||||||
"status": e.Status,
|
|
||||||
}).Error("shim: write exit status")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,9 +121,10 @@ func main() {
|
||||||
if exitShim {
|
if exitShim {
|
||||||
p.delete()
|
p.delete()
|
||||||
p.Wait()
|
p.Wait()
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeInt(path string, i int) error {
|
func writeInt(path string, i int) error {
|
||||||
|
|
|
@ -134,6 +134,11 @@ func (p *process) start() error {
|
||||||
Pdeathsig: syscall.SIGKILL,
|
Pdeathsig: syscall.SIGKILL,
|
||||||
}
|
}
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
|
if exErr, ok := err.(*exec.Error); ok {
|
||||||
|
if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
|
||||||
|
return fmt.Errorf("runc not installed on system")
|
||||||
|
}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.stdio.stdout.Close()
|
p.stdio.stdout.Close()
|
||||||
|
|
|
@ -154,13 +154,9 @@ func (c *container) Start(checkpoint string, s Stdio) (Process, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := cmd.Start(); err != nil {
|
if err := c.startCmd(InitProcessID, cmd, p); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := waitForStart(p, cmd); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.processes[InitProcessID] = p
|
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,14 +194,26 @@ func (c *container) Exec(pid string, pspec specs.ProcessSpec, s Stdio) (pp Proce
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := cmd.Start(); err != nil {
|
if err := c.startCmd(pid, cmd, p); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) error {
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
if exErr, ok := err.(*exec.Error); ok {
|
||||||
|
if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
|
||||||
|
return fmt.Errorf("containerd-shim not installed on system")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := waitForStart(p, cmd); err != nil {
|
if err := waitForStart(p, cmd); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
c.processes[pid] = p
|
c.processes[pid] = p
|
||||||
return p, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *container) getLibctContainer() (libcontainer.Container, error) {
|
func (c *container) getLibctContainer() (libcontainer.Container, error) {
|
||||||
|
@ -312,8 +320,18 @@ func waitForStart(p *process, cmd *exec.Cmd) error {
|
||||||
}
|
}
|
||||||
if !alive {
|
if !alive {
|
||||||
// runc could have failed to run the container so lets get the error
|
// runc could have failed to run the container so lets get the error
|
||||||
// out of the logs
|
// out of the logs or the shim could have encountered an error
|
||||||
messages, err := readLogMessages(filepath.Join(p.root, "log.json"))
|
messages, err := readLogMessages(filepath.Join(p.root, "shim-log.json"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, m := range messages {
|
||||||
|
if m.Level == "error" {
|
||||||
|
return errors.New(m.Msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no errors reported back from shim, check for runc/runtime errors
|
||||||
|
messages, err = readLogMessages(filepath.Join(p.root, "log.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return ErrContainerNotStarted
|
return ErrContainerNotStarted
|
||||||
|
|
Loading…
Reference in a new issue