@@ -24,7 +24,10 @@ import (
24
24
"os"
25
25
"os/exec"
26
26
"path"
27
+ "reflect"
28
+ "strconv"
27
29
"strings"
30
+ "syscall"
28
31
"time"
29
32
30
33
"github.com/golang/glog"
@@ -141,29 +144,20 @@ func isJournaldAvailable() bool {
141
144
}
142
145
143
146
func (es * e2eService ) stop () {
144
- if es .kubeletCmd != nil {
145
- err := es .kubeletCmd .Process .Kill ()
146
- if err != nil {
147
- glog .Errorf ("Failed to stop kubelet.\n %v" , err )
148
- }
147
+ if err := es .stopService ("kubelet" , es .kubeletCmd ); err != nil {
148
+ glog .Errorf ("Failed to stop kubelet: %v" , err )
149
149
}
150
150
if es .kubeletStaticPodDir != "" {
151
151
err := os .RemoveAll (es .kubeletStaticPodDir )
152
152
if err != nil {
153
153
glog .Errorf ("Failed to delete kubelet static pod directory %s.\n %v" , es .kubeletStaticPodDir , err )
154
154
}
155
155
}
156
- if es .apiServerCmd != nil {
157
- err := es .apiServerCmd .Process .Kill ()
158
- if err != nil {
159
- glog .Errorf ("Failed to stop kube-apiserver.\n %v" , err )
160
- }
156
+ if err := es .stopService ("kube-apiserver" , es .apiServerCmd ); err != nil {
157
+ glog .Errorf ("Failed to stop kube-apiserver: %v" , err )
161
158
}
162
- if es .etcdCmd != nil {
163
- err := es .etcdCmd .Process .Kill ()
164
- if err != nil {
165
- glog .Errorf ("Failed to stop etcd.\n %v" , err )
166
- }
159
+ if err := es .stopService ("etcd" , es .etcdCmd ); err != nil {
160
+ glog .Errorf ("Failed to stop etcd: %v" , err )
167
161
}
168
162
if es .etcdDataDir != "" {
169
163
err := os .RemoveAll (es .etcdDataDir )
@@ -250,6 +244,18 @@ func (es *e2eService) startServer(cmd *healthCheckCommand) error {
250
244
cmd .Cmd .Stdout = outfile
251
245
cmd .Cmd .Stderr = outfile
252
246
247
+ // Killing the sudo command should kill the server as well.
248
+ attrs := & syscall.SysProcAttr {}
249
+ // Hack to set linux-only field without build tags.
250
+ deathSigField := reflect .ValueOf (attrs ).Elem ().FieldByName ("Pdeathsig" )
251
+ if deathSigField .IsValid () {
252
+ deathSigField .Set (reflect .ValueOf (syscall .SIGKILL ))
253
+ } else {
254
+ cmdErrorChan <- fmt .Errorf ("Failed to set Pdeathsig field (non-linux build)" )
255
+ return
256
+ }
257
+ cmd .Cmd .SysProcAttr = attrs
258
+
253
259
// Run the command
254
260
err = cmd .Run ()
255
261
if err != nil {
@@ -273,6 +279,48 @@ func (es *e2eService) startServer(cmd *healthCheckCommand) error {
273
279
return fmt .Errorf ("Timeout waiting for service %s" , cmd )
274
280
}
275
281
282
+ func (es * e2eService ) stopService (name string , cmd * exec.Cmd ) error {
283
+ if cmd == nil || cmd .Process == nil {
284
+ glog .V (2 ).Infof ("%s not running" , name )
285
+ return nil
286
+ }
287
+ pid := cmd .Process .Pid
288
+ if pid <= 1 {
289
+ return fmt .Errorf ("invalid PID %d for %s" , pid , name )
290
+ }
291
+
292
+ // Attempt to shut down the process in a friendly manner before forcing it.
293
+ waitChan := make (chan error )
294
+ go func () {
295
+ _ , err := cmd .Process .Wait ()
296
+ waitChan <- err
297
+ close (waitChan )
298
+ }()
299
+
300
+ const timeout = 10 * time .Second
301
+ for _ , signal := range []string {"-TERM" , "-KILL" } {
302
+ glog .V (2 ).Infof ("Killing process %d (%s) with %s" , pid , name , signal )
303
+ _ , err := exec .Command ("sudo" , "kill" , signal , strconv .Itoa (pid )).Output ()
304
+ if err != nil {
305
+ glog .Errorf ("Error signaling process %d (%s) with %s: %v" , pid , name , signal , err )
306
+ continue
307
+ }
308
+
309
+ select {
310
+ case err := <- waitChan :
311
+ if err != nil {
312
+ return fmt .Errorf ("error stopping %s: %v" , name , err )
313
+ }
314
+ // Success!
315
+ return nil
316
+ case <- time .After (timeout ):
317
+ // Continue.
318
+ }
319
+ }
320
+
321
+ return fmt .Errorf ("unable to stop %s" , name )
322
+ }
323
+
276
324
type healthCheckCommand struct {
277
325
* exec.Cmd
278
326
HealthCheckUrl string
0 commit comments