Skip to content

Commit

Permalink
Merge pull request openshift#54 from crosbymichael/report-child-error
Browse files Browse the repository at this point in the history
Report child error to parent
  • Loading branch information
Michael Crosby committed Jun 26, 2014
2 parents f975ff9 + a980a96 commit 53cfe0a
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 16 deletions.
10 changes: 9 additions & 1 deletion namespaces/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
if err != nil {
return -1, err
}
defer syncPipe.Close()

if container.Tty {
master, console, err = system.CreateMasterAndConsole()
Expand All @@ -52,6 +53,9 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
return -1, err
}

// Now we passed the pipe to the child, close our side
syncPipe.CloseChild()

started, err := system.GetProcessStartTime(command.Process.Pid)
if err != nil {
return -1, err
Expand Down Expand Up @@ -90,7 +94,11 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
defer libcontainer.DeleteState(dataPath)

// Sync with child
syncPipe.Close()
if err := syncPipe.ReadFromChild(); err != nil {
command.Process.Kill()
command.Wait()
return -1, err
}

if startCallback != nil {
startCallback()
Expand Down
10 changes: 7 additions & 3 deletions namespaces/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ import (
// Move this to libcontainer package.
// 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.
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)
if err != nil {
return err
Expand All @@ -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
networkState, err := syncPipe.ReadFromParent()
if err != nil {
syncPipe.Close()
return err
}
syncPipe.Close()

if consolePath != "" {
if err := console.OpenAndDup(consolePath); err != nil {
Expand Down
46 changes: 34 additions & 12 deletions namespaces/sync_pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
"syscall"

"github.com/docker/libcontainer/network"
)
Expand All @@ -16,24 +17,17 @@ type SyncPipe struct {
parent, child *os.File
}

func NewSyncPipe() (s *SyncPipe, err 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) {
func NewSyncPipeFromFd(parentFd, childFd uintptr) (*SyncPipe, error) {
s := &SyncPipe{}
if parendFd > 0 {
s.parent = os.NewFile(parendFd, "parendPipe")

if parentFd > 0 {
s.parent = os.NewFile(parentFd, "parentPipe")
} else if childFd > 0 {
s.child = os.NewFile(childFd, "childPipe")
} else {
return nil, fmt.Errorf("no valid sync pipe fd specified")
}

return s, nil
}

Expand All @@ -50,7 +44,22 @@ func (s *SyncPipe) SendToChild(networkState *network.NetworkState) error {
if err != nil {
return err
}

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
}

Expand All @@ -66,15 +75,28 @@ func (s *SyncPipe) ReadFromParent() (*network.NetworkState, error) {
}
}
return networkState, nil
}

func (s *SyncPipe) ReportChildError(err error) {
s.child.Write([]byte(err.Error()))
s.CloseChild()
}

func (s *SyncPipe) Close() error {
if s.parent != nil {
s.parent.Close()
}

if s.child != nil {
s.child.Close()
}

return nil
}

func (s *SyncPipe) CloseChild() {
if s.child != nil {
s.child.Close()
s.child = nil
}
}
20 changes: 20 additions & 0 deletions namespaces/sync_pipe_linux.go
Original file line number Diff line number Diff line change
@@ -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
}
61 changes: 61 additions & 0 deletions namespaces/sync_pipe_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 53cfe0a

Please sign in to comment.