github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/containerizer/system/process_reaper_linux_test.go (about)

     1  package system_test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"syscall"
     8  
     9  	"github.com/cloudfoundry-incubator/garden-linux/containerizer/system"
    10  	"github.com/pivotal-golang/lager"
    11  
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  	"github.com/onsi/gomega/gbytes"
    15  )
    16  
    17  var _ = Describe("ProcessReaper", func() {
    18  	var reaper *system.ProcessReaper
    19  	var waitFunc system.Wait4Func
    20  
    21  	BeforeEach(func() {
    22  		waitFunc = syscall.Wait4
    23  	})
    24  
    25  	JustBeforeEach(func() {
    26  		logger := lager.NewLogger("process_reaper_test_logger")
    27  		logger.RegisterSink(lager.NewWriterSink(GinkgoWriter, lager.ERROR))
    28  		reaper = system.StartReaper(logger, waitFunc)
    29  	})
    30  
    31  	AfterEach(func() {
    32  		reaper.Stop()
    33  	})
    34  
    35  	It("waits for a process to return and returns its exit status", func() {
    36  		cmd := exec.Command("sh", "-c", "exit 3")
    37  		Expect(reaper.Start(cmd)).To(Succeed())
    38  
    39  		Expect(reaper.Wait(cmd)).To(Equal(byte(3)))
    40  	})
    41  
    42  	It("waits for multiple processes", func() {
    43  		cmd1 := exec.Command("sh", "-c", "exit 3")
    44  		cmd2 := exec.Command("sh", "-c", "exit 33")
    45  
    46  		Expect(reaper.Start(cmd1)).To(Succeed())
    47  		Expect(reaper.Start(cmd2)).To(Succeed())
    48  
    49  		Expect(reaper.Wait(cmd1)).To(Equal(byte(3)))
    50  		Expect(reaper.Wait(cmd2)).To(Equal(byte(33)))
    51  	})
    52  
    53  	Context("when there are grandchildren processes", func() {
    54  		It("waits for a process to return and returns its exit status", func() {
    55  			cmd := exec.Command("sh", "-c", "sleep 1; exit 3")
    56  			Expect(reaper.Start(cmd)).To(Succeed())
    57  			Expect(reaper.Wait(cmd)).To(Equal(byte(3)))
    58  		})
    59  
    60  		It("the child process can receive SIGCHLD when a grandchild terminates", func() {
    61  			stdout := gbytes.NewBuffer()
    62  			trap := exec.Command("sh", "-c", "trap 'echo caught SIGCHLD' CHLD; (ls / >/dev/null 2/&1); exit 0")
    63  			trap.Stdout = stdout
    64  
    65  			Expect(reaper.Start(trap)).To(Succeed())
    66  			Expect(reaper.Wait(trap)).To(Equal(byte(0)))
    67  			Eventually(stdout).Should(gbytes.Say("caught SIGCHLD\n"))
    68  		})
    69  	})
    70  
    71  	It("returns correct exit statuses of short-lived processes", func(done Done) {
    72  		for i := 0; i < 100; i++ {
    73  			cmd := exec.Command("sh", "-c", "exit 42")
    74  			Expect(reaper.Start(cmd)).To(Succeed())
    75  
    76  			cmd2 := exec.Command("sh", "-c", "exit 43")
    77  			Expect(reaper.Start(cmd2)).To(Succeed())
    78  
    79  			cmd3 := exec.Command("sh", "-c", "exit 44")
    80  			Expect(reaper.Start(cmd3)).To(Succeed())
    81  
    82  			exitStatus := reaper.Wait(cmd3)
    83  			Expect(exitStatus).To(Equal(byte(44)))
    84  
    85  			exitStatus = reaper.Wait(cmd2)
    86  			Expect(exitStatus).To(Equal(byte(43)))
    87  
    88  			exitStatus = reaper.Wait(cmd)
    89  			Expect(exitStatus).To(Equal(byte(42)))
    90  		}
    91  		close(done)
    92  	}, 90.0)
    93  
    94  	It("reaps processes when they terminate in close succession", func(done Done) {
    95  		for i := 0; i < 100; i++ {
    96  			cmd := exec.Command("sh", "-c", `while true; do sleep 1; done`)
    97  			Expect(reaper.Start(cmd)).To(Succeed())
    98  
    99  			kill := exec.Command("kill", "-9", fmt.Sprintf("%d", cmd.Process.Pid))
   100  			Expect(reaper.Start(kill)).To(Succeed())
   101  
   102  			exitStatus := reaper.Wait(kill)
   103  			Expect(exitStatus).To(Equal(byte(0)))
   104  
   105  			exitStatus = reaper.Wait(cmd)
   106  			Expect(exitStatus).To(Equal(byte(255)))
   107  		}
   108  		close(done)
   109  	}, 90.0)
   110  
   111  	Context("when a container reuses a waited-for pid", func() {
   112  		var nextPid chan int
   113  		var waited chan bool
   114  
   115  		BeforeEach(func() {
   116  			nextPid = make(chan int, 100)
   117  			waited = make(chan bool)
   118  			waitFunc = func(pid int, wstatu *syscall.WaitStatus, options int, rusage *syscall.Rusage) (int, error) {
   119  				waited <- true
   120  				return <-nextPid, nil
   121  			}
   122  		})
   123  
   124  		It("does not deadlock", func(done Done) {
   125  			cmd := exec.Command("sh", "-c", `while true; do sleep 1; done`)
   126  			Expect(reaper.Start(cmd)).To(Succeed())
   127  
   128  			thePid := cmd.Process.Pid
   129  
   130  			nextPid <- thePid
   131  			nextPid <- 0
   132  			syscall.Kill(os.Getpid(), syscall.SIGCHLD)
   133  			Eventually(waited).Should(Receive())
   134  			Eventually(waited).Should(Receive())
   135  
   136  			nextPid <- thePid
   137  			nextPid <- thePid
   138  			nextPid <- 0
   139  			syscall.Kill(os.Getpid(), syscall.SIGCHLD)
   140  
   141  			Eventually(waited).Should(Receive())
   142  			Eventually(waited).Should(Receive())
   143  			Eventually(waited).Should(Receive())
   144  			close(done)
   145  		})
   146  	})
   147  })