@@ -15,6 +15,7 @@ import (
15
15
"os/signal"
16
16
"strings"
17
17
"sync"
18
+ "sync/atomic"
18
19
"syscall"
19
20
"time"
20
21
@@ -77,31 +78,71 @@ var initCmd = &cobra.Command{
77
78
}
78
79
79
80
supervisorDone := make (chan struct {})
81
+ handledByReaper := make (chan int )
82
+ // supervisor is expected to be killed when receiving signals
83
+ ignoreUnexpectedExitCode := atomic.Bool {}
84
+ handleSupervisorExit := func (exitCode int ) {
85
+ if exitCode == 0 {
86
+ return
87
+ }
88
+ logs := extractFailureFromRun ()
89
+ if shared .IsExpectedShutdown (exitCode ) {
90
+ log .Fatal (logs )
91
+ } else {
92
+ if ignoreUnexpectedExitCode .Load () {
93
+ return
94
+ }
95
+ log .WithError (fmt .Errorf (logs )).Fatal ("supervisor run error with unexpected exit code" )
96
+ }
97
+ }
80
98
go func () {
81
99
defer close (supervisorDone )
82
100
83
101
err := runCommand .Wait ()
84
- if err != nil && ! (strings .Contains (err .Error (), "signal: " ) || strings .Contains (err .Error (), "no child processes" )) {
102
+ if err == nil {
103
+ return
104
+ }
105
+ // exited by reaper
106
+ if strings .Contains (err .Error (), "no child processes" ) {
107
+ ctx , cancel := context .WithTimeout (context .Background (), time .Second * 5 )
108
+ defer cancel ()
109
+ select {
110
+ case <- ctx .Done (): // timeout
111
+ case exitCode := <- handledByReaper :
112
+ handleSupervisorExit (exitCode )
113
+ }
114
+ } else if ! (strings .Contains (err .Error (), "signal: " )) {
85
115
if eerr , ok := err .(* exec.ExitError ); ok && eerr .ExitCode () != 0 {
86
- logs := extractFailureFromRun ()
87
- if shared .IsExpectedShutdown (eerr .ExitCode ()) {
88
- log .Fatal (logs )
89
- } else {
90
- log .WithError (fmt .Errorf (logs )).Fatal ("supervisor run error with unexpected exit code" )
91
- }
116
+ handleSupervisorExit (eerr .ExitCode ())
92
117
}
93
118
log .WithError (err ).Error ("supervisor run error" )
94
119
return
95
120
}
96
121
}()
97
122
// start the reaper to clean up zombie processes
98
- reaper .Reap ()
123
+ reaperChan := make (chan reaper.Status , 10 )
124
+ reaper .Start (reaper.Config {
125
+ Pid : - 1 ,
126
+ Options : 0 ,
127
+ DisablePid1Check : false ,
128
+ StatusChannel : reaperChan ,
129
+ })
130
+ go func () {
131
+ for status := range reaperChan {
132
+ if status .Pid != runCommand .Process .Pid {
133
+ continue
134
+ }
135
+ exitCode := status .WaitStatus .ExitStatus ()
136
+ handledByReaper <- exitCode
137
+ }
138
+ }()
99
139
100
140
select {
101
141
case <- supervisorDone :
102
142
// supervisor has ended - we're all done here
103
143
return
104
144
case <- sigInput :
145
+ ignoreUnexpectedExitCode .Store (true )
105
146
// we received a terminating signal - pass on to supervisor and wait for it to finish
106
147
ctx , cancel := context .WithTimeout (context .Background (), cfg .GetTerminationGracePeriod ())
107
148
defer cancel ()
0 commit comments