diff --git a/controller/execute.go b/controller/execute.go index 5782db8daa..debd102988 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -100,7 +100,7 @@ func Execute() { ctx, cancel := context.WithCancel(context.Background()) go serveMetrics(cfg.MetricsAddress) - go handleSigterm(cancel) + setupSigtermHandler(cancel) endpointsSource, err := buildSource(ctx, cfg) if err != nil { @@ -474,14 +474,16 @@ func createDomainFilter(cfg *externaldns.Config) *endpoint.DomainFilter { } } -// handleSigterm listens for a SIGTERM signal and triggers the provided cancel function +// setupSigtermHandler start a routine that listens for a SIGTERM signal and triggers the provided cancel function // to gracefully terminate the application. It logs a message when the signal is received. -func handleSigterm(cancel func()) { +func setupSigtermHandler(cancel func()) { signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGTERM) - <-signals - log.Info("Received SIGTERM. Terminating...") - cancel() + go func() { + <-signals + log.Info("Received SIGTERM. Terminating...") + cancel() + }() } // serveMetrics starts an HTTP server that serves health and metrics endpoints. diff --git a/controller/execute_test.go b/controller/execute_test.go index 84ca2d33f8..4462001651 100644 --- a/controller/execute_test.go +++ b/controller/execute_test.go @@ -24,8 +24,10 @@ import ( "net/http/httptest" "os" "os/exec" + "os/signal" "reflect" "regexp" + "syscall" "testing" "time" @@ -34,6 +36,7 @@ import ( "github.com/stretchr/testify/require" "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/internal/testutils" "sigs.k8s.io/external-dns/pkg/apis/externaldns" "sigs.k8s.io/external-dns/provider" fakeprovider "sigs.k8s.io/external-dns/provider/fakes" @@ -219,6 +222,40 @@ func TestSelectRegistry(t *testing.T) { } } +func TestSetupSigtermHandler(t *testing.T) { + cancelCalled := make(chan bool, 1) + cancel := func() { + cancelCalled <- true + } + + hook := testutils.LogsUnderTestWithLogLevel(log.InfoLevel, t) + + defer signal.Reset(syscall.SIGTERM) + setupSigtermHandler(cancel) + + // Simulate sending a SIGTERM signal + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGTERM) + err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM) + assert.NoError(t, err) + + // Wait for the signal to be received + select { + case sig := <-sigChan: + assert.Equal(t, syscall.SIGTERM, sig) + case <-time.After(1 * time.Second): + t.Fatal("signal was not recieved") + } + + // Wait for the cancel function to be called + select { + case <-cancelCalled: + testutils.TestHelperLogContainsWithLogLevel("Received SIGTERM. Terminating...", log.InfoLevel, hook, t) + case <-time.After(1 * time.Second): + t.Fatal("cancel function was not called") + } +} + // Provider func TestBuildProvider(t *testing.T) { tests := []struct {