blob: b045ef92d0757bfad41204ead66c3e4f25ae7283 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package signals
2
3import (
Cosmos Nicolaoucc581722014-10-07 12:45:39 -07004 "bufio"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07005 "fmt"
Cosmos Nicolaoucc581722014-10-07 12:45:39 -07006 "io"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07007 "os"
Cosmos Nicolaoucc581722014-10-07 12:45:39 -07008 "path/filepath"
9 "runtime"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070010 "syscall"
11 "testing"
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070012 "time"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070013
Jiri Simsa519c5072014-09-17 21:37:57 -070014 "veyron.io/veyron/veyron2"
15 "veyron.io/veyron/veyron2/ipc"
16 "veyron.io/veyron/veyron2/mgmt"
17 "veyron.io/veyron/veyron2/naming"
18 "veyron.io/veyron/veyron2/rt"
19 "veyron.io/veyron/veyron2/services/mgmt/appcycle"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070020
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070021 "veyron.io/veyron/veyron/lib/expect"
22 "veyron.io/veyron/veyron/lib/modules"
Asim Shankarc920db32014-10-16 19:18:21 -070023 "veyron.io/veyron/veyron/lib/testutil"
Jiri Simsa519c5072014-09-17 21:37:57 -070024 "veyron.io/veyron/veyron/lib/testutil/security"
Cosmos Nicolaoud6c3c9c2014-09-30 15:42:53 -070025 "veyron.io/veyron/veyron/profiles"
Jiri Simsa519c5072014-09-17 21:37:57 -070026 vflag "veyron.io/veyron/veyron/security/flag"
27 "veyron.io/veyron/veyron/services/mgmt/node"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070028)
29
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070030// TestHelperProcess is boilerplate for the modules setup.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070031func TestHelperProcess(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070032 modules.DispatchInTest()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070033}
34
35func init() {
Asim Shankarc920db32014-10-16 19:18:21 -070036 testutil.Init()
Cosmos Nicolaou1e78ccc2014-10-09 08:10:26 -070037 modules.RegisterChild("handleDefaults", "", handleDefaults)
38 modules.RegisterChild("handleCustom", "", handleCustom)
39 modules.RegisterChild("handleCustomWithStop", "", handleCustomWithStop)
40 modules.RegisterChild("handleDefaultsIgnoreChan", "", handleDefaultsIgnoreChan)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070041}
42
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070043func stopLoop(stdin io.Reader, ch chan<- struct{}) {
44 scanner := bufio.NewScanner(stdin)
45 for scanner.Scan() {
46 switch scanner.Text() {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070047 case "close":
48 close(ch)
49 return
50 case "stop":
51 rt.R().Stop()
52 }
53 }
54}
55
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070056func program(stdin io.Reader, stdout io.Writer, signals ...os.Signal) {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070057 r := rt.Init()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070058 closeStopLoop := make(chan struct{})
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070059 go stopLoop(stdin, closeStopLoop)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070060 wait := ShutdownOnSignals(signals...)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070061 fmt.Fprintf(stdout, "ready\n")
62 fmt.Fprintf(stdout, "received signal %s\n", <-wait)
Bogdan Caprita4258d882014-07-02 09:15:22 -070063 r.Cleanup()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070064 <-closeStopLoop
65}
66
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070067func handleDefaults(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
68 program(stdin, stdout)
69 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -070070}
71
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070072func handleCustom(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
73 program(stdin, stdout, syscall.SIGABRT)
74 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -070075}
76
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070077func handleCustomWithStop(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
78 program(stdin, stdout, STOP, syscall.SIGABRT, syscall.SIGHUP)
79 return nil
Bogdan Caprita1002ba42014-06-06 19:24:40 -070080}
81
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070082func handleDefaultsIgnoreChan(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Bogdan Caprita4258d882014-07-02 09:15:22 -070083 defer rt.Init().Cleanup()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070084 closeStopLoop := make(chan struct{})
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070085 go stopLoop(stdin, closeStopLoop)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070086 ShutdownOnSignals()
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070087 fmt.Fprintf(stdout, "ready\n")
Jiri Simsa5293dcb2014-05-10 09:56:38 -070088 <-closeStopLoop
Cosmos Nicolaoucc581722014-10-07 12:45:39 -070089 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -070090}
91
92func isSignalInSet(sig os.Signal, set []os.Signal) bool {
93 for _, s := range set {
94 if sig == s {
95 return true
96 }
97 }
98 return false
99}
100
101func checkSignalIsDefault(t *testing.T, sig os.Signal) {
102 if !isSignalInSet(sig, defaultSignals()) {
103 t.Errorf("signal %s not in default signal set, as expected", sig)
104 }
105}
106
107func checkSignalIsNotDefault(t *testing.T, sig os.Signal) {
108 if isSignalInSet(sig, defaultSignals()) {
109 t.Errorf("signal %s unexpectedly in default signal set", sig)
110 }
111}
112
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700113func newShell(t *testing.T, command string) (*modules.Shell, modules.Handle, *expect.Session) {
114 sh := modules.NewShell()
115 sh.AddSubprocess(command, "")
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700116 handle, err := sh.Start(command, nil)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700117 if err != nil {
118 sh.Cleanup(os.Stderr, os.Stderr)
119 t.Fatalf("unexpected error: %s", err)
120 return nil, nil, nil
121 }
122 session := expect.NewSession(t, handle.Stdout(), time.Minute)
123 return sh, handle, session
124}
125
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700126// TestCleanShutdownSignal verifies that sending a signal to a child that
127// handles it by default causes the child to shut down cleanly.
128func TestCleanShutdownSignal(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700129 sh, h, s := newShell(t, "handleDefaults")
130 defer sh.Cleanup(os.Stderr, os.Stderr)
131 s.Expect("ready")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700132 checkSignalIsDefault(t, syscall.SIGINT)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700133 syscall.Kill(h.Pid(), syscall.SIGINT)
134 s.Expectf("received signal %s", syscall.SIGINT)
135 fmt.Fprintf(h.Stdin(), "close\n")
136 s.ExpectEOF()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700137}
138
139// TestCleanShutdownStop verifies that sending a stop comamnd to a child that
140// handles stop commands by default causes the child to shut down cleanly.
141func TestCleanShutdownStop(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700142 sh, h, s := newShell(t, "handleDefaults")
143 defer sh.Cleanup(os.Stderr, os.Stderr)
144 s.Expect("ready")
145 fmt.Fprintf(h.Stdin(), "stop\n")
146 s.Expectf("received signal %s", veyron2.LocalStop)
147 fmt.Fprintf(h.Stdin(), "close\n")
148 s.ExpectEOF()
149
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700150}
151
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700152// TestCleanShutdownStopCustom verifies that sending a stop comamnd to a child
153// that handles stop command as part of a custom set of signals handled, causes
154// the child to shut down cleanly.
155func TestCleanShutdownStopCustom(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700156 sh, h, s := newShell(t, "handleCustomWithStop")
157 defer sh.Cleanup(os.Stderr, os.Stderr)
158 s.Expect("ready")
159 fmt.Fprintf(h.Stdin(), "stop\n")
160 s.Expectf("received signal %s", veyron2.LocalStop)
161 fmt.Fprintf(h.Stdin(), "close\n")
162 s.ExpectEOF()
163}
164
165func testExitStatus(t *testing.T, h modules.Handle, s *expect.Session, code int) {
166 s.ExpectEOF()
167 _, file, line, _ := runtime.Caller(1)
168 file = filepath.Base(file)
169 if got, want := h.Shutdown(os.Stdout, os.Stderr), fmt.Errorf("exit status %d", code); got.Error() != want.Error() {
170 t.Errorf("%s:%d: got %q, want %q", file, line, got, want)
171 }
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700172}
173
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700174// TestStopNoHandler verifies that sending a stop command to a child that does
175// not handle stop commands causes the child to exit immediately.
176func TestStopNoHandler(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700177 sh, h, s := newShell(t, "handleCustom")
178 defer sh.Cleanup(os.Stderr, os.Stderr)
179 s.Expect("ready")
180 fmt.Fprintf(h.Stdin(), "stop\n")
181 testExitStatus(t, h, s, veyron2.UnhandledStopExitCode)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700182}
183
184// TestDoubleSignal verifies that sending a succession of two signals to a child
185// that handles these signals by default causes the child to exit immediately
186// upon receiving the second signal.
187func TestDoubleSignal(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700188 sh, h, s := newShell(t, "handleDefaults")
189 defer sh.Cleanup(os.Stderr, os.Stderr)
190 s.Expect("ready")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700191 checkSignalIsDefault(t, syscall.SIGTERM)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700192 syscall.Kill(h.Pid(), syscall.SIGTERM)
193 s.Expectf("received signal %s", syscall.SIGTERM)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700194 checkSignalIsDefault(t, syscall.SIGINT)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700195 syscall.Kill(h.Pid(), syscall.SIGINT)
196 testExitStatus(t, h, s, DoubleStopExitCode)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700197}
198
199// TestSignalAndStop verifies that sending a signal followed by a stop command
200// to a child that handles these by default causes the child to exit immediately
201// upon receiving the stop command.
202func TestSignalAndStop(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700203 sh, h, s := newShell(t, "handleDefaults")
204 defer sh.Cleanup(os.Stderr, os.Stderr)
205 s.Expect("ready")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700206 checkSignalIsDefault(t, syscall.SIGTERM)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700207 syscall.Kill(h.Pid(), syscall.SIGTERM)
208 s.Expectf("received signal %s", syscall.SIGTERM)
209 fmt.Fprintf(h.Stdin(), "stop\n")
210 testExitStatus(t, h, s, DoubleStopExitCode)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700211}
212
213// TestDoubleStop verifies that sending a succession of stop commands to a child
214// that handles stop commands by default causes the child to exit immediately
215// upon receiving the second stop command.
216func TestDoubleStop(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700217 sh, h, s := newShell(t, "handleDefaults")
218 defer sh.Cleanup(os.Stderr, os.Stderr)
219 s.Expect("ready")
220 fmt.Fprintf(h.Stdin(), "stop\n")
221 s.Expectf("received signal %s", veyron2.LocalStop)
222 fmt.Fprintf(h.Stdin(), "stop\n")
223 testExitStatus(t, h, s, DoubleStopExitCode)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700224}
225
226// TestSendUnhandledSignal verifies that sending a signal that the child does
227// not handle causes the child to exit as per the signal being sent.
228func TestSendUnhandledSignal(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700229 sh, h, s := newShell(t, "handleDefaults")
230 defer sh.Cleanup(os.Stderr, os.Stderr)
231 s.Expect("ready")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700232 checkSignalIsNotDefault(t, syscall.SIGABRT)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700233 syscall.Kill(h.Pid(), syscall.SIGABRT)
234 testExitStatus(t, h, s, 2)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700235}
236
237// TestDoubleSignalIgnoreChan verifies that, even if we ignore the channel that
238// ShutdownOnSignals returns, sending two signals should still cause the
239// process to exit (ensures that there is no dependency in ShutdownOnSignals
240// on having a goroutine read from the returned channel).
241func TestDoubleSignalIgnoreChan(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700242 sh, h, s := newShell(t, "handleDefaultsIgnoreChan")
243 defer sh.Cleanup(os.Stderr, os.Stderr)
244 s.Expect("ready")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700245 // Even if we ignore the channel that ShutdownOnSignals returns,
246 // sending two signals should still cause the process to exit.
247 checkSignalIsDefault(t, syscall.SIGTERM)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700248 syscall.Kill(h.Pid(), syscall.SIGTERM)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700249 checkSignalIsDefault(t, syscall.SIGINT)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700250 syscall.Kill(h.Pid(), syscall.SIGINT)
251 testExitStatus(t, h, s, DoubleStopExitCode)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700252}
253
254// TestHandlerCustomSignal verifies that sending a non-default signal to a
255// server that listens for that signal causes the server to shut down cleanly.
256func TestHandlerCustomSignal(t *testing.T) {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700257 sh, h, s := newShell(t, "handleCustom")
258 defer sh.Cleanup(os.Stderr, os.Stderr)
259 s.Expect("ready")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700260 checkSignalIsNotDefault(t, syscall.SIGABRT)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700261 syscall.Kill(h.Pid(), syscall.SIGABRT)
262 s.Expectf("received signal %s", syscall.SIGABRT)
263 fmt.Fprintf(h.Stdin(), "stop\n")
264 s.ExpectEOF()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700265}
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700266
267// TestHandlerCustomSignalWithStop verifies that sending a custom stop signal
268// to a server that listens for that signal causes the server to shut down
269// cleanly, even when a STOP signal is also among the handled signals.
270func TestHandlerCustomSignalWithStop(t *testing.T) {
Jiri Simsa7fc6e872014-06-09 09:22:53 -0700271 for _, signal := range []syscall.Signal{syscall.SIGABRT, syscall.SIGHUP} {
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700272 sh, h, s := newShell(t, "handleCustomWithStop")
273 s.Expect("ready")
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700274 checkSignalIsNotDefault(t, signal)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700275 syscall.Kill(h.Pid(), signal)
276 s.Expectf("received signal %s", signal)
277 fmt.Fprintf(h.Stdin(), "close\n")
278 s.ExpectEOF()
279 sh.Cleanup(os.Stderr, os.Stderr)
Bogdan Caprita1002ba42014-06-06 19:24:40 -0700280 }
281}
282
283// TestParseSignalsList verifies that ShutdownOnSignals correctly interprets
284// the input list of signals.
285func TestParseSignalsList(t *testing.T) {
286 list := []os.Signal{STOP, syscall.SIGTERM}
287 ShutdownOnSignals(list...)
288 if !isSignalInSet(syscall.SIGTERM, list) {
289 t.Errorf("signal %s not in signal set, as expected: %v", syscall.SIGTERM, list)
290 }
291 if !isSignalInSet(STOP, list) {
292 t.Errorf("signal %s not in signal set, as expected: %v", STOP, list)
293 }
294}
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700295
296type configServer struct {
297 ch chan<- string
298}
299
300func (c *configServer) Set(_ ipc.ServerContext, key, value string) error {
301 if key != mgmt.AppCycleManagerConfigKey {
302 return fmt.Errorf("Unexpected key: %v", key)
303 }
304 c.ch <- value
305 return nil
306
307}
308
309func createConfigServer(t *testing.T) (ipc.Server, string, <-chan string) {
310 server, err := rt.R().NewServer()
311 if err != nil {
312 t.Fatalf("Got error: %v", err)
313 }
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700314 ch := make(chan string)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700315 var ep naming.Endpoint
Cosmos Nicolaouf8d4c2b2014-10-23 22:36:38 -0700316 if ep, err = server.Listen(profiles.LocalListenSpec); err != nil {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700317 t.Fatalf("Got error: %v", err)
318 }
Cosmos Nicolaou8bfacf22014-08-19 11:19:36 -0700319 if err := server.Serve("", ipc.LeafDispatcher(node.NewServerConfig(&configServer{ch}), vflag.NewAuthorizerOrDie())); err != nil {
Cosmos Nicolaoufdc838b2014-06-30 21:44:27 -0700320 t.Fatalf("Got error: %v", err)
321 }
322 return server, naming.JoinAddressName(ep.String(), ""), ch
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700323
324}
325
326// TestCleanRemoteShutdown verifies that remote shutdown works correctly.
327func TestCleanRemoteShutdown(t *testing.T) {
Asim Shankar7b5c94a2014-10-28 21:42:56 -0700328 r := rt.Init()
Bogdan Caprita4258d882014-07-02 09:15:22 -0700329 defer r.Cleanup()
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700330
331 sh := modules.NewShell()
332 sh.AddSubprocess("handleDefaults", "")
333 defer sh.Cleanup(os.Stderr, os.Stderr)
334
Asim Shankar88292912014-10-09 19:41:07 -0700335 // Set the child process up with a blessing from the parent so that
336 // the default authorization works for RPCs between the two.
337 childcreds := security.NewVeyronCredentials(r.Principal(), "child")
338 defer os.RemoveAll(childcreds)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700339 configServer, configServiceName, ch := createConfigServer(t)
340 defer configServer.Stop()
Asim Shankar88292912014-10-09 19:41:07 -0700341 sh.SetVar("VEYRON_CREDENTIALS", childcreds)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700342 sh.SetVar(mgmt.ParentNodeManagerConfigKey, configServiceName)
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700343 h, err := sh.Start("handleDefaults", nil)
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700344 if err != nil {
345 t.Fatalf("unexpected error: %s", err)
346 }
347 s := expect.NewSession(t, h.Stdout(), time.Minute)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700348 appCycleName := <-ch
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700349 s.Expect("ready")
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700350 appCycle, err := appcycle.BindAppCycle(appCycleName)
351 if err != nil {
352 t.Fatalf("Got error: %v", err)
353 }
354 stream, err := appCycle.Stop(r.NewContext())
355 if err != nil {
356 t.Fatalf("Got error: %v", err)
357 }
Shyam Jayaraman97b9dca2014-07-31 13:30:46 -0700358 rStream := stream.RecvStream()
359 if rStream.Advance() || rStream.Err() != nil {
360 t.Errorf("Expected EOF, got (%v, %v) instead: ", rStream.Value(), rStream.Err())
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700361 }
362 if err := stream.Finish(); err != nil {
363 t.Fatalf("Got error: %v", err)
364 }
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700365 s.Expectf("received signal %s", veyron2.RemoteStop)
366 fmt.Fprintf(h.Stdin(), "close\n")
367 s.ExpectEOF()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700368}