diff --git a/subcommand/lifecycle-sidecar/command_ent_test.go b/subcommand/lifecycle-sidecar/command_ent_test.go index 2a945f2352..78075509b6 100644 --- a/subcommand/lifecycle-sidecar/command_ent_test.go +++ b/subcommand/lifecycle-sidecar/command_ent_test.go @@ -4,6 +4,7 @@ package subcommand import ( "os" + "syscall" "testing" "time" @@ -35,7 +36,7 @@ func TestRun_ServicesRegistration_Namespaces(t *testing.T) { "-service-config", configFile, "-sync-period", "100ms", }) - defer stopCommand(t, &cmd, exitChan) + defer stopCommand(t, &cmd, exitChan, syscall.SIGINT) client, err := api.NewClient(&api.Config{ Address: a.HTTPAddr, diff --git a/subcommand/sync-catalog/command.go b/subcommand/sync-catalog/command.go index 9f0f9a0332..aba6f389e4 100644 --- a/subcommand/sync-catalog/command.go +++ b/subcommand/sync-catalog/command.go @@ -381,7 +381,11 @@ func (c *Command) Help() string { // interrupt sends os.Interrupt signal to the command // so it can exit gracefully. This function is needed for tests func (c *Command) interrupt() { - c.sigCh <- os.Interrupt + c.sendSignal(syscall.SIGINT) +} + +func (c *Command) sendSignal(sig os.Signal) { + c.sigCh <- sig } func (c *Command) validateFlags() error { diff --git a/subcommand/sync-catalog/command_ent_test.go b/subcommand/sync-catalog/command_ent_test.go index 80af158ea4..8c1eaba726 100644 --- a/subcommand/sync-catalog/command_ent_test.go +++ b/subcommand/sync-catalog/command_ent_test.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "strings" + "syscall" "testing" "time" @@ -87,7 +88,7 @@ func TestRun_ToConsulSingleDestinationNamespace(t *testing.T) { "-allow-k8s-namespace=*", "-add-k8s-namespace-suffix=false", }) - defer stopCommand(tt, &cmd, exitChan) + defer stopCommand(tt, &cmd, exitChan, syscall.SIGINT) timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} retry.RunWith(timer, tt, func(r *retry.R) { @@ -226,7 +227,7 @@ func TestRun_ToConsulMirroringNamespaces(t *testing.T) { "-k8s-namespace-mirroring-prefix", c.MirroringPrefix, }, c.ExtraFlags...) exitChan := runCommandAsynchronously(&cmd, args) - defer stopCommand(tt, &cmd, exitChan) + defer stopCommand(tt, &cmd, exitChan, syscall.SIGINT) timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} retry.RunWith(timer, tt, func(r *retry.R) { @@ -512,7 +513,7 @@ func TestRun_ToConsulChangingNamespaceFlags(t *testing.T) { require.Equal(r, instances[0].ServiceName, svcName) } }) - stopCommand(tt, &firstCmd, exitChan) + stopCommand(tt, &firstCmd, exitChan, syscall.SIGINT) } tt.Log("first command run complete") @@ -528,7 +529,7 @@ func TestRun_ToConsulChangingNamespaceFlags(t *testing.T) { }), } exitChan := runCommandAsynchronously(&secondCmd, append(commonArgs, c.SecondRunFlags...)) - defer stopCommand(tt, &secondCmd, exitChan) + defer stopCommand(tt, &secondCmd, exitChan, syscall.SIGINT) // Wait until the expected services are synced and the old ones // deleted. @@ -693,7 +694,7 @@ func TestRun_ToConsulNamespacesACLs(t *testing.T) { "-consul-cross-namespace-acl-policy=cross-namespace-policy", } exitChan := runCommandAsynchronously(&cmd, append(commonArgs, c.Flags...)) - defer stopCommand(tt, &cmd, exitChan) + defer stopCommand(tt, &cmd, exitChan, syscall.SIGINT) // Check the namespaces are created correctly timer = &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} diff --git a/subcommand/sync-catalog/command_test.go b/subcommand/sync-catalog/command_test.go index 3c62a2b68b..6144d05e91 100644 --- a/subcommand/sync-catalog/command_test.go +++ b/subcommand/sync-catalog/command_test.go @@ -2,6 +2,8 @@ package synccatalog import ( "context" + "os" + "syscall" "testing" "time" @@ -70,7 +72,7 @@ func TestRun_Defaults_SyncsConsulServiceToK8s(t *testing.T) { exitChan := runCommandAsynchronously(&cmd, []string{ "-http-addr", testServer.HTTPAddr, }) - defer stopCommand(t, &cmd, exitChan) + defer stopCommand(t, &cmd, exitChan, syscall.SIGINT) retry.Run(t, func(r *retry.R) { serviceList, err := k8s.CoreV1().Services(metav1.NamespaceDefault).List(context.Background(), metav1.ListOptions{}) @@ -81,6 +83,35 @@ func TestRun_Defaults_SyncsConsulServiceToK8s(t *testing.T) { }) } +// Test that the command exits cleanly on signals +func TestRun_ExitCleanlyOnSignals(t *testing.T) { + t.Run("SIGINT", testSignalHandling(syscall.SIGINT)) + t.Run("SIGTERM", testSignalHandling(syscall.SIGTERM)) +} + +func testSignalHandling(sig os.Signal) func(*testing.T) { + return func(t *testing.T) { + k8s, testServer := completeSetup(t) + defer testServer.Stop() + + // Run the command. + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + logger: hclog.New(&hclog.LoggerOptions{ + Name: t.Name(), + Level: hclog.Debug, + }), + } + + exitChan := runCommandAsynchronously(&cmd, []string{ + "-http-addr", testServer.HTTPAddr, + }) + defer stopCommand(t, &cmd, exitChan, sig) + } +} + // Test that when -add-k8s-namespace-suffix flag is used // k8s namespaces are appended to the service names synced to Consul func TestRun_ToConsulWithAddK8SNamespaceSuffix(t *testing.T) { @@ -116,7 +147,7 @@ func TestRun_ToConsulWithAddK8SNamespaceSuffix(t *testing.T) { "-consul-write-interval", "100ms", "-add-k8s-namespace-suffix", }) - defer stopCommand(t, &cmd, exitChan) + defer stopCommand(t, &cmd, exitChan, syscall.SIGINT) retry.Run(t, func(r *retry.R) { services, _, err := consulClient.Catalog().Services(nil) @@ -167,14 +198,14 @@ func TestCommand_Run_ToConsulChangeAddK8SNamespaceSuffixToTrue(t *testing.T) { require.Contains(r, services, "foo") }) - stopCommand(t, &cmd, exitChan) + stopCommand(t, &cmd, exitChan, syscall.SIGINT) // restart sync with -add-k8s-namespace-suffix exitChan = runCommandAsynchronously(&cmd, []string{ "-consul-write-interval", "100ms", "-add-k8s-namespace-suffix", }) - defer stopCommand(t, &cmd, exitChan) + defer stopCommand(t, &cmd, exitChan, syscall.SIGINT) // check that the name of the service is now namespaced retry.Run(t, func(r *retry.R) { @@ -223,7 +254,7 @@ func TestCommand_Run_ToConsulTwoServicesSameNameDifferentNamespace(t *testing.T) "-consul-write-interval", "100ms", "-add-k8s-namespace-suffix", }) - defer stopCommand(t, &cmd, exitChan) + defer stopCommand(t, &cmd, exitChan, syscall.SIGINT) // check that the name of the service is namespaced retry.Run(t, func(r *retry.R) { @@ -333,7 +364,7 @@ func TestRun_ToConsulAllowDenyLists(t *testing.T) { }), } exitChan := runCommandAsynchronously(&cmd, flags) - defer stopCommand(tt, &cmd, exitChan) + defer stopCommand(tt, &cmd, exitChan, syscall.SIGINT) retry.Run(tt, func(r *retry.R) { svcs, _, err := consulClient.Catalog().Services(nil) @@ -488,7 +519,7 @@ func TestRun_ToConsulChangingFlags(t *testing.T) { require.Equal(r, instances[0].ServiceName, svcName) } }) - stopCommand(tt, &firstCmd, exitChan) + stopCommand(tt, &firstCmd, exitChan, syscall.SIGINT) } tt.Log("first command run complete") @@ -504,7 +535,7 @@ func TestRun_ToConsulChangingFlags(t *testing.T) { }), } exitChan := runCommandAsynchronously(&secondCmd, append(commonArgs, c.SecondRunFlags...)) - defer stopCommand(tt, &secondCmd, exitChan) + defer stopCommand(tt, &secondCmd, exitChan, syscall.SIGINT) // Wait until the expected services are synced and the old ones // deleted. @@ -559,9 +590,9 @@ func runCommandAsynchronously(cmd *Command, args []string) chan int { return exitChan } -func stopCommand(t *testing.T, cmd *Command, exitChan chan int) { +func stopCommand(t *testing.T, cmd *Command, exitChan chan int, sig os.Signal) { if len(exitChan) == 0 { - cmd.interrupt() + cmd.sendSignal(sig) } select { case c := <-exitChan: