@@ -327,20 +327,28 @@ func InitConfig(ctx context.Context, clients *shared.ClientFactory, rootCmd *cob
327327// It listens for process interrupts and sends to IOStreams' GetInterruptChannel() for use in
328328// in communicating process interrupts elsewhere in the code.
329329func ExecuteContext (ctx context.Context , rootCmd * cobra.Command , clients * shared.ClientFactory ) {
330+ // Derive a cancel context that is cancelled when the main execution is interrupted or cleaned up.
331+ // Sub-commands can register for the cleanup wait group with clients.CleanupWaitGroup.Add(1)
332+ // and listen for <-ctx.Done() to be notified when the main execution is interrupted, in order
333+ // to have a chance to cleanup. This is useful for long running processes and background goroutines,
334+ // such as the activity and upgrade commands.
330335 ctx , cancel := context .WithCancel (ctx )
331336
332337 completedChan := make (chan bool , 1 ) // completed is used for signalling an end to command
333338 exitChan := make (chan bool , 1 ) // exit blocks the command from exiting until completed
334339 interruptChan := make (chan os.Signal , 1 ) // interrupt catches signals to avoid abrupt exits
340+
335341 signal .Notify (interruptChan , os .Interrupt , syscall .SIGTERM )
336342 defer func () {
337343 signal .Stop (interruptChan )
338344 cancel ()
339345 }()
346+
347+ // Wait for either an interrupt signal (ctrl+c) or an explicit call to ctx.cancel()
340348 go func () {
341- // Wait for either an interrupt signal (ctrl+c), or an explicit call to ctx.cancel()
342349 select {
343- case <- interruptChan : // first interrupt signal, cancel context
350+ // Received interrupt signal, so cancel the context and cleanup
351+ case <- interruptChan :
344352 clients .IO .PrintInfo (ctx , false , "\n " ) // flush the CTRL + C character
345353 clients .IO .PrintDebug (ctx , "Got process interrupt signal, cancelling context" )
346354 cancel ()
@@ -355,18 +363,17 @@ func ExecuteContext(ctx context.Context, rootCmd *cobra.Command, clients *shared
355363 clients .IO .PrintDebug (ctx , "Exiting with cancel exit code." )
356364 os .Exit (int (iostreams .ExitCancel ))
357365 }()
358- case <- ctx .Done ():
359- // No interrupt signal sent, command executed to completion
360- // FIXME - `.Done()` channel is triggered by `cancel()` and not a successfully completion
366+ // Received completed execution, so exit the process successfully
361367 case <- completedChan :
362- // No canceled context, but command has completed execution
363368 exitChan <- true
364369 }
370+
365371 // If we get a second interrupt, no matter what exit the process
366372 <- interruptChan
367373 clients .IO .PrintDebug (ctx , "Got second process interrupt signal, exiting the process" )
368374 os .Exit (int (iostreams .ExitCancel ))
369375 }()
376+
370377 // The cleanup() method in the root command will invoke via `defer` from within Execute.
371378 if err := rootCmd .ExecuteContext (ctx ); err != nil {
372379 errMsg := err .Error ()
@@ -389,6 +396,7 @@ func ExecuteContext(ctx context.Context, rootCmd *cobra.Command, clients *shared
389396 } else {
390397 completedChan <- true
391398 }
399+
392400 <- exitChan
393401 _ = clients .EventTracker .FlushToLogstash (ctx , clients .Config , clients .IO , clients .IO .GetExitCode ())
394402}
0 commit comments