diff --git a/command/run/run.go b/command/run/run.go index cf5e55fa..84c6c5a0 100644 --- a/command/run/run.go +++ b/command/run/run.go @@ -282,6 +282,7 @@ func (c *command) runE(cmd *cobra.Command, _ []string) (cmdErr error) { defer func() { if err := goleak.Find(); err != nil { fmt.Fprintf(cmd.ErrOrStderr(), "goleak: %s", err) + os.Exit(1) } }() } diff --git a/e2e/forwarder/service.go b/e2e/forwarder/service.go index f0f39217..387ec00f 100644 --- a/e2e/forwarder/service.go +++ b/e2e/forwarder/service.go @@ -34,6 +34,7 @@ func ProxyService() *Service { Image: Image, Environment: map[string]string{ "FORWARDER_API_ADDRESS": ":10000", + "FORWARDER_GOLEAK": enabled, }, } } diff --git a/e2e/sc-2450/server.py b/e2e/sc-2450/server.py index 467e91f0..45524cbb 100644 --- a/e2e/sc-2450/server.py +++ b/e2e/sc-2450/server.py @@ -2,6 +2,8 @@ import os import time +import signal +import sys from http.server import BaseHTTPRequestHandler, HTTPServer # Headers taken from `failed.pcapng` @@ -108,9 +110,21 @@ def do_GET(self): self.wfile.write(self.data[15:]) print("Done") +def signal_handler(signum, frame): + print("Received signal to terminate. Exiting gracefully...") + sys.exit(0) if __name__ == "__main__": port = 8307 print("Listening on 0.0.0.0:%s" % port) server = HTTPServer(("", port), RequestHandler) - server.serve_forever() + + signal.signal(signal.SIGTERM, signal_handler) + + try: + server.serve_forever() + except KeyboardInterrupt: + pass + finally: + print("Shutting down server.") + server.server_close() diff --git a/e2e/setup/runner.go b/e2e/setup/runner.go index 7d96db85..04ca7241 100644 --- a/e2e/setup/runner.go +++ b/e2e/setup/runner.go @@ -189,7 +189,20 @@ func (r *Runner) runSetup(s *Setup) (runErr error) { } // Run the test service. - return cmd.Up("--force-recreate", "--exit-code-from", TestServiceName, TestServiceName) + if err := cmd.Up("--force-recreate", "--exit-code-from", TestServiceName, TestServiceName); err != nil { + return err + } + + if !r.Debug { + if err := cmd.Stop(); err != nil { + return fmt.Errorf("compose stop: %w", err) + } + if err := exitedSuccessfully(cmd, r.services(s)); err != nil { + return fmt.Errorf("services failed to exit: %w", err) + } + } + + return nil } func (r *Runner) services(s *Setup) []string { @@ -202,3 +215,16 @@ func (r *Runner) services(s *Setup) []string { } return res } + +func exitedSuccessfully(cmd *compose.Command, services []string) error { + for _, srv := range services { + code, err := cmd.ServiceExitCode(srv) + if err != nil { + return fmt.Errorf("service %s failed to exit: %w", srv, err) + } + if code != 0 { + return fmt.Errorf("service %s exited with code %d", srv, code) + } + } + return nil +} diff --git a/http_proxy.go b/http_proxy.go index a8641859..ab13c3ce 100644 --- a/http_proxy.go +++ b/http_proxy.go @@ -581,5 +581,10 @@ func (hp *HTTPProxy) Addr() string { func (hp *HTTPProxy) Close() error { err := hp.listener.Close() hp.proxy.Close() + + if tr, ok := hp.transport.(*http.Transport); ok { + tr.CloseIdleConnections() + } + return err } diff --git a/utils/compose/cmd.go b/utils/compose/cmd.go index f2a769b7..d158935e 100644 --- a/utils/compose/cmd.go +++ b/utils/compose/cmd.go @@ -14,6 +14,7 @@ import ( "os/exec" "path" "slices" + "strconv" "strings" "time" ) @@ -102,6 +103,39 @@ func (c *Command) Up(args ...string) error { return c.run(c.cmd("up", args)) } +func (c *Command) Stop(args ...string) error { + return c.quietRun(c.cmd("stop", args)) +} + +func (c *Command) ServiceExitCode(service string) (int, error) { + args := []string{ + "inspect", + "--format", + "{{.State.Status}},{{.State.ExitCode}}", + c.serviceContainerName(service), + } + + cmd := exec.Command(c.rt, args...) //nolint:gosec // this is a command runner + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + stdout.WriteTo(c.stdout) + stderr.WriteTo(c.stderr) + return 0, err + } + + state, code, ok := strings.Cut(strings.TrimSpace(stdout.String()), ",") + if !ok { + return 0, fmt.Errorf("unexpected output: %s", stdout.String()) + } + if state != "exited" { + return 0, fmt.Errorf("unexpected state: %s", state) + } + return strconv.Atoi(code) +} + func (c *Command) Down(args ...string) error { return c.quietRun(c.cmd("down", args)) }