Merge pull request #54 from crosbymichael/report-child-error
Report child error to parent
This commit is contained in:
commit
53cfe0a1eb
|
@ -32,6 +32,7 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
defer syncPipe.Close()
|
||||||
|
|
||||||
if container.Tty {
|
if container.Tty {
|
||||||
master, console, err = system.CreateMasterAndConsole()
|
master, console, err = system.CreateMasterAndConsole()
|
||||||
|
@ -52,6 +53,9 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now we passed the pipe to the child, close our side
|
||||||
|
syncPipe.CloseChild()
|
||||||
|
|
||||||
started, err := system.GetProcessStartTime(command.Process.Pid)
|
started, err := system.GetProcessStartTime(command.Process.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -90,7 +94,11 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
|
||||||
defer libcontainer.DeleteState(dataPath)
|
defer libcontainer.DeleteState(dataPath)
|
||||||
|
|
||||||
// Sync with child
|
// Sync with child
|
||||||
syncPipe.Close()
|
if err := syncPipe.ReadFromChild(); err != nil {
|
||||||
|
command.Process.Kill()
|
||||||
|
command.Wait()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
if startCallback != nil {
|
if startCallback != nil {
|
||||||
startCallback()
|
startCallback()
|
||||||
|
|
|
@ -27,7 +27,13 @@ import (
|
||||||
// Move this to libcontainer package.
|
// Move this to libcontainer package.
|
||||||
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
||||||
// and other options required for the new container.
|
// and other options required for the new container.
|
||||||
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error {
|
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
syncPipe.ReportChildError(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -42,10 +48,8 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
|
||||||
// We always read this as it is a way to sync with the parent as well
|
// We always read this as it is a way to sync with the parent as well
|
||||||
networkState, err := syncPipe.ReadFromParent()
|
networkState, err := syncPipe.ReadFromParent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syncPipe.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
syncPipe.Close()
|
|
||||||
|
|
||||||
if consolePath != "" {
|
if consolePath != "" {
|
||||||
if err := console.OpenAndDup(consolePath); err != nil {
|
if err := console.OpenAndDup(consolePath); err != nil {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
)
|
)
|
||||||
|
@ -16,24 +17,17 @@ type SyncPipe struct {
|
||||||
parent, child *os.File
|
parent, child *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSyncPipe() (s *SyncPipe, err error) {
|
func NewSyncPipeFromFd(parentFd, childFd uintptr) (*SyncPipe, error) {
|
||||||
s = &SyncPipe{}
|
|
||||||
s.child, s.parent, err = os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSyncPipeFromFd(parendFd, childFd uintptr) (*SyncPipe, error) {
|
|
||||||
s := &SyncPipe{}
|
s := &SyncPipe{}
|
||||||
if parendFd > 0 {
|
|
||||||
s.parent = os.NewFile(parendFd, "parendPipe")
|
if parentFd > 0 {
|
||||||
|
s.parent = os.NewFile(parentFd, "parentPipe")
|
||||||
} else if childFd > 0 {
|
} else if childFd > 0 {
|
||||||
s.child = os.NewFile(childFd, "childPipe")
|
s.child = os.NewFile(childFd, "childPipe")
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("no valid sync pipe fd specified")
|
return nil, fmt.Errorf("no valid sync pipe fd specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +44,22 @@ func (s *SyncPipe) SendToChild(networkState *network.NetworkState) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.parent.Write(data)
|
s.parent.Write(data)
|
||||||
|
|
||||||
|
return syscall.Shutdown(int(s.parent.Fd()), syscall.SHUT_WR)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SyncPipe) ReadFromChild() error {
|
||||||
|
data, err := ioutil.ReadAll(s.parent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) > 0 {
|
||||||
|
return fmt.Errorf("%s", data)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,15 +75,28 @@ func (s *SyncPipe) ReadFromParent() (*network.NetworkState, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return networkState, nil
|
return networkState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SyncPipe) ReportChildError(err error) {
|
||||||
|
s.child.Write([]byte(err.Error()))
|
||||||
|
s.CloseChild()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncPipe) Close() error {
|
func (s *SyncPipe) Close() error {
|
||||||
if s.parent != nil {
|
if s.parent != nil {
|
||||||
s.parent.Close()
|
s.parent.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.child != nil {
|
if s.child != nil {
|
||||||
s.child.Close()
|
s.child.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SyncPipe) CloseChild() {
|
||||||
|
if s.child != nil {
|
||||||
|
s.child.Close()
|
||||||
|
s.child = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewSyncPipe() (s *SyncPipe, err error) {
|
||||||
|
s = &SyncPipe{}
|
||||||
|
|
||||||
|
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.child = os.NewFile(uintptr(fds[0]), "child syncpipe")
|
||||||
|
s.parent = os.NewFile(uintptr(fds[1]), "parent syncpipe")
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSendErrorFromChild(t *testing.T) {
|
||||||
|
pipe, err := NewSyncPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := pipe.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
expected := "something bad happened"
|
||||||
|
|
||||||
|
pipe.ReportChildError(fmt.Errorf(expected))
|
||||||
|
|
||||||
|
childError := pipe.ReadFromChild()
|
||||||
|
if childError == nil {
|
||||||
|
t.Fatal("expected an error to be returned but did not receive anything")
|
||||||
|
}
|
||||||
|
|
||||||
|
if childError.Error() != expected {
|
||||||
|
t.Fatalf("expected %q but received error message %q", expected, childError.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendPayloadToChild(t *testing.T) {
|
||||||
|
pipe, err := NewSyncPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := pipe.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
expected := "libcontainer"
|
||||||
|
|
||||||
|
if err := pipe.SendToChild(&network.NetworkState{VethHost: expected}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := pipe.ReadFromParent()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.VethHost != expected {
|
||||||
|
t.Fatalf("expected veth host %q but received %q", expected, payload.VethHost)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue