Merge branch 'master' into kill-rogue-processes
diff --git a/gexec/session.go b/gexec/session.go
index 46e7122..1dd5068 100644
--- a/gexec/session.go
+++ b/gexec/session.go
@@ -94,6 +94,7 @@
go session.monitorForExit(exited)
}
+ trackedSessions = append(trackedSessions, session)
return session, err
}
@@ -179,7 +180,7 @@
}
/*
-Terminate sends the running command the passed in signal. It does not wait for the process to exit.
+Signal sends the running command the passed in signal. It does not wait for the process to exit.
If the command has already exited, Signal returns silently.
@@ -212,3 +213,90 @@
close(exited)
}
+
+var trackedSessions = []*Session{}
+var trackedSessionsMutex = &sync.Mutex{}
+
+/*
+Kill sends a SIGKILL signal to all the processes started by Run, and waits for them to exit.
+The timeout specified is applied to each process killed.
+
+If any of the processes already exited, KillAndWait returns silently.
+*/
+func KillAndWait(timeout ...interface{}) {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Kill().Wait(timeout...)
+ }
+}
+
+/*
+Kill sends a SIGTERM signal to all the processes started by Run, and waits for them to exit.
+The timeout specified is applied to each process killed.
+
+If any of the processes already exited, TerminateAndWait returns silently.
+*/
+func TerminateAndWait(timeout ...interface{}) {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Terminate().Wait(timeout...)
+ }
+}
+
+/*
+Kill sends a SIGKILL signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Kill returns silently.
+*/
+func Kill() {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Kill()
+ }
+}
+
+/*
+Terminate sends a SIGTERM signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Terminate returns silently.
+*/
+func Terminate() {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Terminate()
+ }
+}
+
+/*
+Signal sends the passed in signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Signal returns silently.
+*/
+func Signal(signal os.Signal) {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Signal(signal)
+ }
+}
+
+/*
+Interrupt sends the SIGINT signal to all the processes started by Run.
+It does not wait for the processes to exit.
+
+If any of the processes already exited, Interrupt returns silently.
+*/
+func Interrupt() {
+ trackedSessionsMutex.Lock()
+ defer trackedSessionsMutex.Unlock()
+ for _, session := range trackedSessions {
+ session.Interrupt()
+ }
+}
diff --git a/gexec/session_test.go b/gexec/session_test.go
index 13cc3cd..5a7af74 100644
--- a/gexec/session_test.go
+++ b/gexec/session_test.go
@@ -84,7 +84,7 @@
})
Describe("kill", func() {
- It("should kill the command and wait for it to exit", func() {
+ It("should kill the command and don't wait for it to exit", func() {
session, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
Ω(err).ShouldNot(HaveOccurred())
@@ -127,6 +127,140 @@
})
})
+ Context("tracking sessions", func() {
+ BeforeEach(func() {
+ KillAndWait()
+ })
+
+ Describe("kill", func() {
+ It("should kill all the started sessions, and not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Kill()
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session2).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session3).ShouldNot(Exit(), "Should not exit immediately...")
+
+ Eventually(session1).Should(Exit(128 + 9))
+ Eventually(session2).Should(Exit(128 + 9))
+ Eventually(session3).Should(Exit(128 + 9))
+ })
+ })
+
+ Describe("killAndWait", func() {
+ It("should kill all the started sessions and wait for them to finish", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ KillAndWait()
+ Ω(session1).Should(Exit(128+9), "Should have exited")
+ Ω(session2).Should(Exit(128+9), "Should have exited")
+ Ω(session3).Should(Exit(128+9), "Should have exited")
+ })
+ })
+
+ Describe("terminate", func() {
+ It("should terminate all the started sessions, and not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Terminate()
+
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session2).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session3).ShouldNot(Exit(), "Should not exit immediately...")
+
+ Eventually(session1).Should(Exit(128 + 15))
+ Eventually(session2).Should(Exit(128 + 15))
+ Eventually(session3).Should(Exit(128 + 15))
+ })
+ })
+
+ Describe("terminateAndWait", func() {
+ It("should terminate all the started sessions, and wait for them to exit", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ TerminateAndWait()
+
+ Ω(session1).Should(Exit(128+15), "Should have exited")
+ Ω(session2).Should(Exit(128+15), "Should have exited")
+ Ω(session3).Should(Exit(128+15), "Should have exited")
+ })
+ })
+
+ Describe("signal", func() {
+ It("should signal all the started sessions, and not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Signal(syscall.SIGABRT)
+
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session2).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session3).ShouldNot(Exit(), "Should not exit immediately...")
+
+ Eventually(session1).Should(Exit(128 + 6))
+ Eventually(session2).Should(Exit(128 + 6))
+ Eventually(session3).Should(Exit(128 + 6))
+ })
+ })
+
+ Describe("interrupt", func() {
+ It("should interrupt all the started sessions, and not wait", func() {
+ session1, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session2, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ session3, err := Start(exec.Command("sleep", "10000000"), GinkgoWriter, GinkgoWriter)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Interrupt()
+
+ Ω(session1).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session2).ShouldNot(Exit(), "Should not exit immediately...")
+ Ω(session3).ShouldNot(Exit(), "Should not exit immediately...")
+
+ Eventually(session1).Should(Exit(128 + 2))
+ Eventually(session2).Should(Exit(128 + 2))
+ Eventually(session3).Should(Exit(128 + 2))
+ })
+ })
+ })
+
Context("when the command exits", func() {
It("should close the buffers", func() {
Eventually(session).Should(Exit())