Skip to content

Commit 04b6ada

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 8851544 commit 04b6ada

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

controller/execute.go

Lines changed: 8 additions & 6 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 {
@@ -474,14 +474,16 @@ func createDomainFilter(cfg *externaldns.Config) *endpoint.DomainFilter {
474474
}
475475
}
476476

477-
// handleSigterm listens for a SIGTERM signal and triggers the provided cancel function
477+
// setupSigtermHandler start a routine that listens for a SIGTERM signal and triggers the provided cancel function
478478
// to gracefully terminate the application. It logs a message when the signal is received.
479-
func handleSigterm(cancel func()) {
479+
func setupSigtermHandler(cancel func()) {
480480
signals := make(chan os.Signal, 1)
481481
signal.Notify(signals, syscall.SIGTERM)
482-
<-signals
483-
log.Info("Received SIGTERM. Terminating...")
484-
cancel()
482+
go func() {
483+
<-signals
484+
log.Info("Received SIGTERM. Terminating...")
485+
cancel()
486+
}()
485487
}
486488

487489
// serveMetrics starts an HTTP server that serves health and metrics endpoints.

controller/execute_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
"net/http/httptest"
2525
"os"
2626
"os/exec"
27+
"os/signal"
2728
"reflect"
2829
"regexp"
30+
"syscall"
2931
"testing"
3032
"time"
3133

@@ -34,6 +36,7 @@ import (
3436
"github.com/stretchr/testify/require"
3537

3638
"sigs.k8s.io/external-dns/endpoint"
39+
"sigs.k8s.io/external-dns/internal/testutils"
3740
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
3841
"sigs.k8s.io/external-dns/provider"
3942
fakeprovider "sigs.k8s.io/external-dns/provider/fakes"
@@ -219,6 +222,40 @@ func TestSelectRegistry(t *testing.T) {
219222
}
220223
}
221224

225+
func TestSetupSigtermHandler(t *testing.T) {
226+
cancelCalled := make(chan bool, 1)
227+
cancel := func() {
228+
cancelCalled <- true
229+
}
230+
231+
hook := testutils.LogsUnderTestWithLogLevel(log.InfoLevel, t)
232+
233+
defer signal.Reset(syscall.SIGTERM)
234+
setupSigtermHandler(cancel)
235+
236+
// Simulate sending a SIGTERM signal
237+
sigChan := make(chan os.Signal, 1)
238+
signal.Notify(sigChan, syscall.SIGTERM)
239+
err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
240+
assert.NoError(t, err)
241+
242+
// Wait for the signal to be received
243+
select {
244+
case sig := <-sigChan:
245+
assert.Equal(t, syscall.SIGTERM, sig)
246+
case <-time.After(1 * time.Second):
247+
t.Fatal("signal was not recieved")
248+
}
249+
250+
// Wait for the cancel function to be called
251+
select {
252+
case <-cancelCalled:
253+
testutils.TestHelperLogContainsWithLogLevel("Received SIGTERM. Terminating...", log.InfoLevel, hook, t)
254+
case <-time.After(1 * time.Second):
255+
t.Fatal("cancel function was not called")
256+
}
257+
}
258+
222259
// Provider
223260
func TestBuildProvider(t *testing.T) {
224261
tests := []struct {

0 commit comments

Comments
 (0)