Skip to content

Commit ad9a3b1

Browse files
committed
fix(controller): race condition in handleSigterm() test
Fix a race condition between the signal handler setup and the signal send in the test for handleSigterm()
1 parent 4de8bd6 commit ad9a3b1

File tree

2 files changed

+26
-6
lines changed

2 files changed

+26
-6
lines changed

controller/execute.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func Execute() {
100100
ctx, cancel := context.WithCancel(context.Background())
101101

102102
go serveMetrics(cfg.MetricsAddress)
103-
go handleSigterm(cancel)
103+
setupSigtermHandler(cancel)
104104

105105
endpointsSource, err := buildSource(ctx, cfg)
106106
if err != nil {
@@ -470,14 +470,23 @@ func createDomainFilter(cfg *externaldns.Config) *endpoint.DomainFilter {
470470

471471
// handleSigterm listens for a SIGTERM signal and triggers the provided cancel function
472472
// to gracefully terminate the application. It logs a message when the signal is received.
473-
func handleSigterm(cancel func()) {
473+
// The setupCh channel is used to signal when the signal handler is ready to receive signals.
474+
func handleSigterm(cancel func(), setupCh chan struct{}) {
474475
signals := make(chan os.Signal, 1)
475476
signal.Notify(signals, syscall.SIGTERM)
477+
close(setupCh)
476478
<-signals
477479
log.Info("Received SIGTERM. Terminating...")
478480
cancel()
479481
}
480482

483+
// setupSigtermHandler initializes the SIGTERM handler in a separate goroutine and waits for it to be ready.
484+
func setupSigtermHandler(cancel func()) {
485+
setupCh := make(chan struct{})
486+
go handleSigterm(cancel, setupCh)
487+
<-setupCh
488+
}
489+
481490
// serveMetrics starts an HTTP server that serves health and metrics endpoints.
482491
// The /healthz endpoint returns a 200 OK status to indicate the service is healthy.
483492
// The /metrics endpoint serves Prometheus metrics.

controller/execute_test.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,11 @@ func TestCreateDomainFilter(t *testing.T) {
189189
}
190190
}
191191

192-
func TestHandleSigterm(t *testing.T) {
192+
func TestSetupSigterm(t *testing.T) {
193+
t.Cleanup(func() {
194+
signal.Reset(syscall.SIGTERM)
195+
})
196+
193197
cancelCalled := make(chan bool, 1)
194198
cancel := func() {
195199
cancelCalled <- true
@@ -199,23 +203,30 @@ func TestHandleSigterm(t *testing.T) {
199203
log.SetOutput(&logOutput)
200204
defer log.SetOutput(os.Stderr)
201205

202-
go handleSigterm(cancel)
206+
setupSigtermHandler(cancel)
203207

204208
// Simulate sending a SIGTERM signal
205209
sigChan := make(chan os.Signal, 1)
206210
signal.Notify(sigChan, syscall.SIGTERM)
207211
err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
208212
assert.NoError(t, err)
209213

214+
// Wait for the signal to be received
215+
select {
216+
case sig := <-sigChan:
217+
assert.Equal(t, syscall.SIGTERM, sig)
218+
case <-time.After(1 * time.Second):
219+
t.Fatal("signal was not recieved")
220+
}
221+
210222
// Wait for the cancel function to be called
211223
select {
212224
case <-cancelCalled:
213225
assert.Contains(t, logOutput.String(), "Received SIGTERM. Terminating...")
214-
case sig := <-sigChan:
215-
assert.Equal(t, syscall.SIGTERM, sig)
216226
case <-time.After(1 * time.Second):
217227
t.Fatal("cancel function was not called")
218228
}
229+
219230
}
220231

221232
func getRandomPort() (int, error) {

0 commit comments

Comments
 (0)