@@ -95,6 +95,13 @@ const (
95
95
KindGit = "git"
96
96
)
97
97
98
+ type ShutdownReason int16
99
+
100
+ const (
101
+ ShutdownReasonSuccess ShutdownReason = 0
102
+ ShutdownReasonExecutionError ShutdownReason = 1
103
+ )
104
+
98
105
// Run serves as main entrypoint to the supervisor
99
106
func Run (options ... RunOption ) {
100
107
defer log .Info ("supervisor shut down" )
@@ -148,7 +155,7 @@ func Run(options ...RunOption) {
148
155
149
156
ctx , cancel := context .WithCancel (context .Background ())
150
157
var (
151
- shutdown = make (chan struct {} )
158
+ shutdown = make (chan ShutdownReason , 1 )
152
159
ideReady = & ideReadyState {cond : sync .NewCond (& sync.Mutex {})}
153
160
cstate = NewInMemoryContentState (cfg .RepoRoot )
154
161
gitpodService = createGitpodService (cfg , tokenService )
@@ -202,7 +209,7 @@ func Run(options ...RunOption) {
202
209
// When in terminating mode, the reaper will send SIGTERM to each child that gets reparented
203
210
// to us and is still running. We use this mechanism to send SIGTERM to a shell child processes
204
211
// that get reparented once their parent shell terminates during shutdown.
205
- terminatingReaper := make (chan bool )
212
+ terminatingReaper := make (chan bool , 1 )
206
213
// We keep the reaper until the bitter end because:
207
214
// - it doesn't need graceful shutdown
208
215
// - we want to do as much work as possible (SIGTERM'ing reparented processes during shutdown).
@@ -220,11 +227,15 @@ func Run(options ...RunOption) {
220
227
wg .Add (1 )
221
228
go startSSHServer (ctx , cfg , & wg )
222
229
wg .Add (1 )
223
- go taskManager .Run (ctx , & wg )
230
+ tasksSuccessChan := make (chan bool , 1 )
231
+ go taskManager .Run (ctx , & wg , tasksSuccessChan )
224
232
wg .Add (1 )
225
233
go socketActivationForDocker (ctx , & wg , termMux )
226
234
227
- if ! cfg .isHeadless () {
235
+ if cfg .isHeadless () {
236
+ wg .Add (1 )
237
+ go stopWhenTasksAreDone (ctx , & wg , shutdown , tasksSuccessChan )
238
+ } else {
228
239
wg .Add (1 )
229
240
go portMgmt .Run (ctx , & wg )
230
241
}
@@ -236,18 +247,20 @@ func Run(options ...RunOption) {
236
247
}
237
248
238
249
log .Error ("metadata access is possible - shutting down" )
239
- close ( shutdown )
250
+ shutdown <- ShutdownReasonExecutionError
240
251
}()
241
252
}
242
253
243
254
sigChan := make (chan os.Signal , 1 )
244
255
signal .Notify (sigChan , os .Interrupt , syscall .SIGTERM )
256
+ var exitCode int
245
257
select {
246
258
case <- sigChan :
247
- case <- shutdown :
259
+ case shutdownReason := <- shutdown :
260
+ exitCode = int (shutdownReason )
248
261
}
249
262
250
- log .Info ("received SIGTERM - tearing down" )
263
+ log .Info ("received SIGTERM (or shutdown) - tearing down" )
251
264
terminatingReaper <- true
252
265
cancel ()
253
266
err = termMux .Close ()
@@ -260,6 +273,9 @@ func Run(options ...RunOption) {
260
273
terminateChildProcesses ()
261
274
262
275
wg .Wait ()
276
+
277
+ log .WithField ("exitCode" , exitCode ).Debug ("supervisor exit" )
278
+ os .Exit (exitCode )
263
279
}
264
280
265
281
func createGitpodService (cfg * Config , tknsrv api.TokenServiceServer ) * gitpod.APIoverJSONRPC {
@@ -772,6 +788,22 @@ func tunnelOverSSH(ctx context.Context, tunneled *ports.TunneledPortsService, ne
772
788
<- ctx .Done ()
773
789
}
774
790
791
+ func stopWhenTasksAreDone (ctx context.Context , wg * sync.WaitGroup , shutdown chan ShutdownReason , successChan <- chan bool ) {
792
+ defer wg .Done ()
793
+ defer close (shutdown )
794
+
795
+ success := <- successChan
796
+ if ! success {
797
+ // we signal task failure via kubernetes termination log
798
+ msg := []byte ("headless task failed" )
799
+ err := ioutil .WriteFile ("/dev/termination-log" , msg , 0644 )
800
+ if err != nil {
801
+ log .WithError (err ).Error ("err while writing termination log" )
802
+ }
803
+ }
804
+ shutdown <- ShutdownReasonSuccess
805
+ }
806
+
775
807
func startSSHServer (ctx context.Context , cfg * Config , wg * sync.WaitGroup ) {
776
808
defer wg .Done ()
777
809
0 commit comments