diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-09-23 10:48:35 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-09-23 10:48:35 +0300 |
commit | c13d9d902ef8175a0b1165ef0bc8643fb37b7897 (patch) | |
tree | a113990da37b7d7cdce457de8c1298e636bff350 | |
parent | 9498ab9459048cc595d8e2e411b027d080c0ab0f (diff) | |
parent | 5cccaa21826849eb3b57a32325e9cc3ec19c5e40 (diff) |
Merge branch 'pks-golangci-lint-errcheck-improvements' into 'master'
golangci-lint: Stop excluding many `Close` and `Serve` functions from errcheck linter
See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4810
Merged-by: Patrick Steinhardt <psteinhardt@gitlab.com>
Approved-by: Quang-Minh Nguyen <qmnguyen@gitlab.com>
Approved-by: Pavlo Strokov <pstrokov@gitlab.com>
42 files changed, 219 insertions, 92 deletions
diff --git a/.golangci.yml b/.golangci.yml index 2c667e980..f97d22a09 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -46,6 +46,26 @@ linters-settings: include-go-root: true packages-with-error-message: - io/ioutil: "ioutil is deprecated starting with Go 1.16" + errcheck: + # The following are functions for which we are currently not consistently + # checking returned errors. This is not intended as a list of known-okay + # cases to skip the checks, but rather as a list of things we should + # eventually fix. + exclude-functions: + - (*database/sql.DB).Close + - (*database/sql.Rows).Close + - (*gitlab.com/gitlab-org/gitaly/v15/client.Pool).Close + - (*gitlab.com/gitlab-org/gitaly/v15/internal/sidechannel.ServerConn).Close + - (*gitlab.com/gitlab-org/gitaly/v15/internal/streamcache.pipe).Close + - (*gitlab.com/gitlab-org/gitaly/v15/internal/streamcache.pipeReader).Close + - (*google.golang.org/grpc.ClientConn).Close + - (*google.golang.org/grpc.ServerConn).Close + - (*io.PipeReader).Close + - (*io.PipeWriter).Close + - (*os.File).Close + - (io.Closer).Close + - (net.Conn).Close + - (net.Listener).Close forbidigo: forbid: # Tests and code which use timing-based setups have repeatedly resulted @@ -95,9 +115,6 @@ issues: - forbidigo # This fine thing excludes all paths which don't end with "_test.go". path: "^([^_]|_([^t]|t([^e]|e([^s]|s([^t]|t([^\\.]|\\.([^g]|g[^o])))))))*$" - - linters: - - errcheck - text: "Error return value of `[^`]+.(Close|Serve)` is not checked" # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. diff --git a/client/dial_test.go b/client/dial_test.go index 758ff1013..e0870ee32 100644 --- a/client/dial_test.go +++ b/client/dial_test.go @@ -247,7 +247,7 @@ func TestDialSidechannel(t *testing.T) { return nil }) - defer scw.Close() + defer testhelper.MustClose(t, scw) req := &healthpb.HealthCheckRequest{Service: "test sidechannel"} _, err = healthpb.NewHealthClient(conn).Check(ctx, req) @@ -296,7 +296,7 @@ func TestDial_Correlation(t *testing.T) { } grpc_testing.RegisterTestServiceServer(grpcServer, svc) - go func() { assert.NoError(t, grpcServer.Serve(listener)) }() + go testhelper.MustServe(t, grpcServer, listener) defer grpcServer.Stop() ctx := testhelper.Context(t) @@ -332,7 +332,7 @@ func TestDial_Correlation(t *testing.T) { } grpc_testing.RegisterTestServiceServer(grpcServer, svc) - go func() { assert.NoError(t, grpcServer.Serve(listener)) }() + go testhelper.MustServe(t, grpcServer, listener) defer grpcServer.Stop() ctx := testhelper.Context(t) @@ -395,7 +395,7 @@ func TestDial_Tracing(t *testing.T) { } grpc_testing.RegisterTestServiceServer(grpcServer, svc) - go func() { require.NoError(t, grpcServer.Serve(listener)) }() + go testhelper.MustServe(t, grpcServer, listener) defer grpcServer.Stop() ctx := testhelper.Context(t) @@ -543,7 +543,7 @@ func startTCPListener(tb testing.TB, factory func(credentials.TransportCredentia address := fmt.Sprintf("%d", tcpPort) grpcServer := factory(insecure.NewCredentials()) - go grpcServer.Serve(listener) + go testhelper.MustServe(tb, grpcServer, listener) return func() { grpcServer.Stop() @@ -558,7 +558,7 @@ func startUnixListener(tb testing.TB, factory func(credentials.TransportCredenti require.NoError(tb, err) grpcServer := factory(insecure.NewCredentials()) - go grpcServer.Serve(listener) + go testhelper.MustServe(tb, grpcServer, listener) return func() { grpcServer.Stop() @@ -583,7 +583,7 @@ func startTLSListener(tb testing.TB, factory func(credentials.TransportCredentia MinVersion: tls.VersionTLS12, }), ) - go grpcServer.Serve(listener) + go testhelper.MustServe(tb, grpcServer, listener) return func() { grpcServer.Stop() diff --git a/client/upload_pack.go b/client/upload_pack.go index 863465068..aeda65f6b 100644 --- a/client/upload_pack.go +++ b/client/upload_pack.go @@ -43,14 +43,24 @@ func UploadPack(ctx context.Context, conn *grpc.ClientConn, stdin io.Reader, std // UploadPackWithSidechannel proxies an SSH git-upload-pack (git fetch) // session to Gitaly using a sidechannel for the raw data transfer. -func UploadPackWithSidechannel(ctx context.Context, conn *grpc.ClientConn, reg *SidechannelRegistry, stdin io.Reader, stdout, stderr io.Writer, req *gitalypb.SSHUploadPackWithSidechannelRequest) (int32, error) { +func UploadPackWithSidechannel( + ctx context.Context, + conn *grpc.ClientConn, + reg *SidechannelRegistry, + stdin io.Reader, + stdout, stderr io.Writer, + req *gitalypb.SSHUploadPackWithSidechannelRequest, +) (int32, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() ctx, wt := reg.Register(ctx, func(c SidechannelConn) error { return stream.ProxyPktLine(c, stdin, stdout, stderr) }) - defer wt.Close() + defer func() { + // We aleady check the error further down. + _ = wt.Close() + }() sshClient := gitalypb.NewSSHServiceClient(conn) if _, err := sshClient.SSHUploadPackWithSidechannel(ctx, req); err != nil { diff --git a/cmd/gitaly-hooks/hooks.go b/cmd/gitaly-hooks/hooks.go index 39d0e4ba4..11601d29a 100644 --- a/cmd/gitaly-hooks/hooks.go +++ b/cmd/gitaly-hooks/hooks.go @@ -334,7 +334,10 @@ func handlePackObjectsWithSidechannel(ctx context.Context, payload git.HooksPayl if err != nil { return fmt.Errorf("SetupSidechannel: %w", err) } - defer wt.Close() + defer func() { + // We aleady check the error further down. + _ = wt.Close() + }() var glID, glUsername, gitProtocol string @@ -376,5 +379,13 @@ func handlePackObjectsWithSidechannel(ctx context.Context, payload git.HooksPayl return fmt.Errorf("call PackObjectsHookWithSidechannel: %w", err) } - return wt.Wait() + if err := wt.Wait(); err != nil { + return err + } + + if err := wt.Close(); err != nil { + return fmt.Errorf("closing sidechannel: %w", err) + } + + return nil } diff --git a/cmd/gitaly-ssh/auth_test.go b/cmd/gitaly-ssh/auth_test.go index 8a5921432..db33fbab3 100644 --- a/cmd/gitaly-ssh/auth_test.go +++ b/cmd/gitaly-ssh/auth_test.go @@ -167,7 +167,7 @@ func runServer(t *testing.T, secure bool, cfg config.Cfg, connectionType string, listener, err := net.Listen(connectionType, addr) require.NoError(t, err) - go srv.Serve(listener) + go testhelper.MustServe(t, srv, listener) port := 0 if connectionType != "unix" { diff --git a/internal/backchannel/backchannel_test.go b/internal/backchannel/backchannel_test.go index e8885629a..534b63962 100644 --- a/internal/backchannel/backchannel_test.go +++ b/internal/backchannel/backchannel_test.go @@ -78,7 +78,7 @@ func TestBackchannel_concurrentRequestsFromMultipleClients(t *testing.T) { }) defer srv.Stop() - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) ctx := testhelper.Context(t) start := make(chan struct{}) @@ -271,7 +271,7 @@ func Benchmark(b *testing.B) { require.NoError(b, err) defer srv.Stop() - go srv.Serve(ln) + go testhelper.MustServe(b, srv, ln) ctx := testhelper.Context(b) opts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())} diff --git a/internal/backchannel/server.go b/internal/backchannel/server.go index 9e0f0537a..9b7e58bda 100644 --- a/internal/backchannel/server.go +++ b/internal/backchannel/server.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io" "net" "github.com/hashicorp/yamux" @@ -136,10 +137,17 @@ func (s *ServerHandshaker) Handshake(conn net.Conn, authInfo credentials.AuthInf Conn: clientToServerStream, close: func() error { s.registry.RemoveBackchannel(id) - backchannelConn.Close() - muxSession.Close() - logger.Close() - return nil + + var firstErr error + for _, closer := range []io.Closer{ + backchannelConn, muxSession, logger, + } { + if err := closer.Close(); err != nil && firstErr == nil { + firstErr = err + } + } + + return firstErr }, }, withSessionInfo(authInfo, id, muxSession), diff --git a/internal/cache/diskcache.go b/internal/cache/diskcache.go index 192afde07..60408f4af 100644 --- a/internal/cache/diskcache.go +++ b/internal/cache/diskcache.go @@ -272,7 +272,7 @@ func (irc instrumentedReadCloser) Read(p []byte) (n int, err error) { // PutStream will store a stream in a repo-namespace keyed by the digest of the // request protobuf message. -func (c *DiskCache) PutStream(ctx context.Context, repo *gitalypb.Repository, req proto.Message, src io.Reader) error { +func (c *DiskCache) PutStream(ctx context.Context, repo *gitalypb.Repository, req proto.Message, src io.Reader) (returnedErr error) { reqPath, err := c.KeyPath(ctx, repo, req) if err != nil { return err @@ -298,7 +298,13 @@ func (c *DiskCache) PutStream(ctx context.Context, repo *gitalypb.Repository, re if err != nil { return err } - defer sf.Close() + defer func() { + if err := sf.Close(); err != nil && returnedErr == nil { + if !errors.Is(err, safe.ErrAlreadyDone) { + returnedErr = err + } + } + }() n, err = io.Copy(sf, src) if err != nil { diff --git a/internal/cache/keyer.go b/internal/cache/keyer.go index b89b240db..519edbd38 100644 --- a/internal/cache/keyer.go +++ b/internal/cache/keyer.go @@ -56,7 +56,7 @@ func newLeaseKeyer(locator storage.Locator, countErr func(error) error) leaseKey } } -func (keyer leaseKeyer) updateLatest(ctx context.Context, repo *gitalypb.Repository) (string, error) { +func (keyer leaseKeyer) updateLatest(ctx context.Context, repo *gitalypb.Repository) (_ string, returnedErr error) { repoStatePath, err := keyer.getRepoStatePath(repo) if err != nil { return "", err @@ -71,7 +71,13 @@ func (keyer leaseKeyer) updateLatest(ctx context.Context, repo *gitalypb.Reposit if err != nil { return "", err } - defer latest.Close() + defer func() { + if err := latest.Close(); err != nil && returnedErr == nil { + if !errors.Is(err, safe.ErrAlreadyDone) { + returnedErr = err + } + } + }() nextGenID := uuid.New().String() if nextGenID == "" { diff --git a/internal/git/gittest/http_server.go b/internal/git/gittest/http_server.go index d55f2987a..a0ac490f3 100644 --- a/internal/git/gittest/http_server.go +++ b/internal/git/gittest/http_server.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v15/internal/git" + "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" ) // HTTPServer starts an HTTP server with git-http-backend(1) as CGI handler. The repository is @@ -43,7 +44,7 @@ func HTTPServer(ctx context.Context, tb testing.TB, gitCmdFactory git.CommandFac }) } - go s.Serve(listener) + go testhelper.MustServe(tb, &s, listener) return listener.Addr().(*net.TCPAddr).Port, s.Close } diff --git a/internal/gitaly/archive/tar_builder.go b/internal/gitaly/archive/tar_builder.go index 2ae1a0b32..3e93e273e 100644 --- a/internal/gitaly/archive/tar_builder.go +++ b/internal/gitaly/archive/tar_builder.go @@ -196,7 +196,7 @@ func (t *TarBuilder) Close() error { if t.err != nil { // Ignore any close error in favour of reporting the previous one, but // ensure the tar writer is closed to avoid resource leaks - t.tarWriter.Close() + _ = t.tarWriter.Close() return t.err } diff --git a/internal/gitaly/client/dial_test.go b/internal/gitaly/client/dial_test.go index 9c9f72f17..2ff52e9b8 100644 --- a/internal/gitaly/client/dial_test.go +++ b/internal/gitaly/client/dial_test.go @@ -44,7 +44,7 @@ func TestDial(t *testing.T) { ln, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) ctx := testhelper.Context(t) t.Run("non-muxed conn", func(t *testing.T) { diff --git a/internal/gitaly/hook/sidechannel.go b/internal/gitaly/hook/sidechannel.go index 62c32a059..14fa5304e 100644 --- a/internal/gitaly/hook/sidechannel.go +++ b/internal/gitaly/hook/sidechannel.go @@ -107,7 +107,10 @@ func (wt *SidechannelWaiter) run(callback func(*net.UnixConn) error) { if err != nil { return err } - defer c.Close() + defer func() { + // Error is already checked below. + _ = c.Close() + }() // Eagerly remove the socket directory, in case the process exits before // wt.Close() can run. @@ -115,7 +118,15 @@ func (wt *SidechannelWaiter) run(callback func(*net.UnixConn) error) { return err } - return callback(c) + if err := callback(c); err != nil { + return err + } + + if err := c.Close(); err != nil { + return err + } + + return nil }() } diff --git a/internal/gitaly/hook/sidechannel_test.go b/internal/gitaly/hook/sidechannel_test.go index 243e0de09..0cba065c9 100644 --- a/internal/gitaly/hook/sidechannel_test.go +++ b/internal/gitaly/hook/sidechannel_test.go @@ -47,7 +47,7 @@ func testSidechannelWithRuntimeDir(t *testing.T, runtimeDir string) { }, ) require.NoError(t, err) - defer wt.Close() + defer testhelper.MustClose(t, wt) require.DirExists(t, wt.socketDir) @@ -85,7 +85,6 @@ func testSidechannelCleanupWithRuntimeDir(t *testing.T, runtimeDir string) { func(c *net.UnixConn) error { return nil }, ) require.NoError(t, err) - defer wt.Close() require.DirExists(t, wt.socketDir) _ = wt.Close() diff --git a/internal/gitaly/linguist/language_stats.go b/internal/gitaly/linguist/language_stats.go index e65a90c9a..cefa3b644 100644 --- a/internal/gitaly/linguist/language_stats.go +++ b/internal/gitaly/linguist/language_stats.go @@ -135,7 +135,10 @@ func (c *languageStats) save(repo *localrepo.Repo, commitID string) error { }() w := zlib.NewWriter(file) - defer w.Close() + defer func() { + // We already check the error further down. + _ = w.Close() + }() if err = json.NewEncoder(w).Encode(c); err != nil { return fmt.Errorf("languageStats encode json: %w", err) diff --git a/internal/gitaly/server/auth_test.go b/internal/gitaly/server/auth_test.go index 7cd8d9efa..a997282cb 100644 --- a/internal/gitaly/server/auth_test.go +++ b/internal/gitaly/server/auth_test.go @@ -225,7 +225,7 @@ func runServer(t *testing.T, cfg config.Cfg) string { listener, err := net.Listen("unix", serverSocketPath) require.NoError(t, err) t.Cleanup(srv.Stop) - go srv.Serve(listener) + go testhelper.MustServe(t, srv, listener) return "unix://" + serverSocketPath } @@ -256,7 +256,7 @@ func runSecureServer(t *testing.T, cfg config.Cfg) string { listener, hostPort := testhelper.GetLocalhostListener(t) t.Cleanup(srv.Stop) - go srv.Serve(listener) + go testhelper.MustServe(t, srv, listener) return hostPort } diff --git a/internal/gitaly/server/server_factory_test.go b/internal/gitaly/server/server_factory_test.go index 9a181089d..0137b62a5 100644 --- a/internal/gitaly/server/server_factory_test.go +++ b/internal/gitaly/server/server_factory_test.go @@ -44,6 +44,8 @@ func TestGitalyServerFactory(t *testing.T) { if schema == starter.TLS { srv, err := sf.CreateExternal(true) require.NoError(t, err) + t.Cleanup(srv.Stop) + healthpb.RegisterHealthServer(srv, health.NewServer()) listener, err := net.Listen(starter.TCP, addr) @@ -66,6 +68,8 @@ func TestGitalyServerFactory(t *testing.T) { } else { srv, err := sf.CreateExternal(false) require.NoError(t, err) + t.Cleanup(srv.Stop) + healthpb.RegisterHealthServer(srv, health.NewServer()) listener, err := net.Listen(schema, addr) @@ -299,7 +303,7 @@ func TestGitalyServerFactory_closeOrder(t *testing.T) { require.NoError(t, err) defer ln.Close() - go server.Serve(ln) + go testhelper.MustServe(t, server, ln) conn, err := grpc.DialContext(ctx, ln.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) diff --git a/internal/gitaly/service/hook/pack_objects_test.go b/internal/gitaly/service/hook/pack_objects_test.go index 0bf4262da..403f3fe77 100644 --- a/internal/gitaly/service/hook/pack_objects_test.go +++ b/internal/gitaly/service/hook/pack_objects_test.go @@ -134,7 +134,7 @@ func testServerPackObjectsHookSeparateContextWithRuntimeDir(t *testing.T, ctx co }, ) require.NoError(t, err) - defer wt1.Close() + defer testhelper.MustClose(t, wt1) go func() { defer wg.Done() @@ -174,7 +174,7 @@ func testServerPackObjectsHookSeparateContextWithRuntimeDir(t *testing.T, ctx co }, ) require.NoError(t, err) - defer wt2.Close() + defer testhelper.MustClose(t, wt2) go func() { defer wg.Done() @@ -240,7 +240,7 @@ func testServerPackObjectsHookUsesCache(t *testing.T, ctx context.Context, runti }, ) require.NoError(t, err) - defer wt.Close() + defer testhelper.MustClose(t, wt) client, conn := newHooksClient(t, cfg.SocketPath) defer conn.Close() @@ -346,7 +346,7 @@ func testServerPackObjectsHookWithSidechannelWithRuntimeDir(t *testing.T, ctx co }, ) require.NoError(t, err) - defer wt.Close() + defer testhelper.MustClose(t, wt) client, conn := newHooksClient(t, cfg.SocketPath) defer conn.Close() @@ -539,7 +539,7 @@ func testServerPackObjectsHookWithSidechannelCanceledWithRuntimeDir(t *testing.T }, ) require.NoError(t, err) - defer wt.Close() + defer testhelper.MustClose(t, wt) cfg.SocketPath = runHooksServer(t, cfg, nil) repo, _ := gittest.CreateRepository(ctx, t, cfg, gittest.CreateRepositoryConfig{ diff --git a/internal/gitaly/service/repository/apply_gitattributes.go b/internal/gitaly/service/repository/apply_gitattributes.go index 460a86ba5..700b2b6c2 100644 --- a/internal/gitaly/service/repository/apply_gitattributes.go +++ b/internal/gitaly/service/repository/apply_gitattributes.go @@ -22,7 +22,7 @@ import ( const attributesFileMode os.FileMode = 0o644 -func (s *server) applyGitattributes(ctx context.Context, repo *localrepo.Repo, objectReader catfile.ObjectReader, repoPath string, revision []byte) error { +func (s *server) applyGitattributes(ctx context.Context, repo *localrepo.Repo, objectReader catfile.ObjectReader, repoPath string, revision []byte) (returnedErr error) { infoPath := filepath.Join(repoPath, "info") attributesPath := filepath.Join(infoPath, "attributes") @@ -85,7 +85,13 @@ func (s *server) applyGitattributes(ctx context.Context, repo *localrepo.Repo, o if err != nil { return fmt.Errorf("creating gitattributes writer: %w", err) } - defer writer.Close() + defer func() { + if err := writer.Close(); err != nil && returnedErr == nil { + if !errors.Is(err, safe.ErrAlreadyDone) { + returnedErr = err + } + } + }() if _, err := io.Copy(writer, blobObj); err != nil { return err diff --git a/internal/gitaly/service/smarthttp/upload_pack_test.go b/internal/gitaly/service/smarthttp/upload_pack_test.go index 02ca6296e..1da5527da 100644 --- a/internal/gitaly/service/smarthttp/upload_pack_test.go +++ b/internal/gitaly/service/smarthttp/upload_pack_test.go @@ -6,6 +6,7 @@ import ( "bytes" "context" "encoding/binary" + "errors" "fmt" "io" "path/filepath" @@ -613,7 +614,6 @@ func makePostUploadPackWithSidechannelRequest(ctx context.Context, t *testing.T, return <-errC }) - defer waiter.Close() rpcRequest := &gitalypb.PostUploadPackWithSidechannelRequest{ Repository: in.GetRepository(), @@ -622,7 +622,11 @@ func makePostUploadPackWithSidechannelRequest(ctx context.Context, t *testing.T, } _, err := client.PostUploadPackWithSidechannel(ctxOut, rpcRequest) if err == nil { - require.NoError(t, waiter.Close()) + testhelper.MustClose(t, waiter) + } else if err := waiter.Close(); err != nil && !errors.Is(err, sidechannel.ErrCallbackDidNotRun) { + // When the request failed the sidechannel may not even have been used, so we need + // to catch the `ErrCallbackDidNotRun` error here. + require.NoError(t, err) } return responseBuffer, err diff --git a/internal/gitaly/storage/metadata.go b/internal/gitaly/storage/metadata.go index 1af49493c..f5d8e51e9 100644 --- a/internal/gitaly/storage/metadata.go +++ b/internal/gitaly/storage/metadata.go @@ -2,6 +2,7 @@ package storage import ( "encoding/json" + "errors" "os" "path/filepath" @@ -21,7 +22,7 @@ type Metadata struct { } // WriteMetadataFile marshals and writes a metadata file -func WriteMetadataFile(storagePath string) error { +func WriteMetadataFile(storagePath string) (returnedErr error) { path := filepath.Join(storagePath, metadataFilename) if _, err := os.Stat(path); !os.IsNotExist(err) { @@ -32,7 +33,13 @@ func WriteMetadataFile(storagePath string) error { if err != nil { return err } - defer fw.Close() + defer func() { + if err := fw.Close(); err != nil && returnedErr == nil { + if !errors.Is(err, safe.ErrAlreadyDone) { + returnedErr = err + } + } + }() if err = json.NewEncoder(fw).Encode(&Metadata{ GitalyFilesystemID: uuid.New().String(), diff --git a/internal/gitlab/test_server.go b/internal/gitlab/test_server.go index b23e4b251..3a6a78eb0 100644 --- a/internal/gitlab/test_server.go +++ b/internal/gitlab/test_server.go @@ -569,7 +569,7 @@ func startSocketHTTPServer(tb testing.TB, mux *http.ServeMux, tlsCfg *tls.Config TLSConfig: tlsCfg, } - go server.Serve(socketListener) + go testhelper.MustServe(tb, &server, socketListener) url := "http+unix://" + filename cleanup := func() { diff --git a/internal/helper/chunk/chunker_test.go b/internal/helper/chunk/chunker_test.go index ea759312e..e80be6419 100644 --- a/internal/helper/chunk/chunker_test.go +++ b/internal/helper/chunk/chunker_test.go @@ -85,7 +85,7 @@ func runServer(t *testing.T, s *server, opt ...grpc.ServerOption) (*grpc.Server, lis, err := net.Listen("unix", serverSocketPath) require.NoError(t, err) - go grpcServer.Serve(lis) + go testhelper.MustServe(t, grpcServer, lis) return grpcServer, "unix://" + serverSocketPath } diff --git a/internal/middleware/limithandler/middleware_test.go b/internal/middleware/limithandler/middleware_test.go index d5aa4ee57..08c3f0cca 100644 --- a/internal/middleware/limithandler/middleware_test.go +++ b/internal/middleware/limithandler/middleware_test.go @@ -572,7 +572,7 @@ func runServer(t *testing.T, s pb.TestServer, opt ...grpc.ServerOption) (*grpc.S lis, err := net.Listen("unix", serverSocketPath) require.NoError(t, err) - go grpcServer.Serve(lis) + go testhelper.MustServe(t, grpcServer, lis) return grpcServer, "unix://" + serverSocketPath } diff --git a/internal/praefect/auth_test.go b/internal/praefect/auth_test.go index 9f0cd9012..324392dce 100644 --- a/internal/praefect/auth_test.go +++ b/internal/praefect/auth_test.go @@ -169,7 +169,7 @@ func runServer(t *testing.T, token string, required bool) (*grpc.Server, string, listener, err := net.Listen("unix", serverSocketPath) require.NoError(t, err) - go srv.Serve(listener) + go testhelper.MustServe(t, srv, listener) return srv, "unix://" + serverSocketPath, cleanup } diff --git a/internal/praefect/delete_object_pool_test.go b/internal/praefect/delete_object_pool_test.go index db63edddf..5e29bf0b1 100644 --- a/internal/praefect/delete_object_pool_test.go +++ b/internal/praefect/delete_object_pool_test.go @@ -45,10 +45,10 @@ func TestDeleteObjectPoolHandler(t *testing.T) { secondaryLn, secondaryAddr := testhelper.GetLocalhostListener(t) defer primarySrv.Stop() - go primarySrv.Serve(primaryLn) + go testhelper.MustServe(t, primarySrv, primaryLn) defer secondarySrv.Stop() - go secondarySrv.Serve(secondaryLn) + go testhelper.MustServe(t, secondarySrv, secondaryLn) db := testdb.New(t) rs := datastore.NewPostgresRepositoryStore(db, nil) @@ -96,7 +96,7 @@ func TestDeleteObjectPoolHandler(t *testing.T) { }, struct{}{}) defer praefectSrv.Stop() - go praefectSrv.Serve(praefectLn) + go testhelper.MustServe(t, praefectSrv, praefectLn) praefectConn, err := grpc.Dial(praefectAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) diff --git a/internal/praefect/grpc-proxy/proxy/handler_ext_test.go b/internal/praefect/grpc-proxy/proxy/handler_ext_test.go index 7e5234fcd..a20131ca5 100644 --- a/internal/praefect/grpc-proxy/proxy/handler_ext_test.go +++ b/internal/praefect/grpc-proxy/proxy/handler_ext_test.go @@ -344,7 +344,7 @@ func TestProxyErrorPropagation(t *testing.T) { backendServer := grpc.NewServer(grpc.UnknownServiceHandler(func(interface{}, grpc.ServerStream) error { return tc.backendError })) - go func() { backendServer.Serve(backendListener) }() + go testhelper.MustServe(t, backendServer, backendListener) defer backendServer.Stop() ctx := testhelper.Context(t) @@ -374,7 +374,7 @@ func TestProxyErrorPropagation(t *testing.T) { })), ) - go func() { proxyServer.Serve(proxyListener) }() + go testhelper.MustServe(t, proxyServer, proxyListener) defer proxyServer.Stop() proxyClientConn, err := grpc.DialContext(ctx, "unix://"+proxyListener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) @@ -488,7 +488,7 @@ func TestRegisterStreamHandlers(t *testing.T) { proxy.RegisterStreamHandlers(server, grpc_testing.TestService_ServiceDesc.ServiceName, registeredHandlers) listener := newListener(t) - go server.Serve(listener) + go testhelper.MustServe(t, server, listener) defer server.Stop() conn, err := client.Dial("tcp://"+listener.Addr().String(), []grpc.DialOption{grpc.WithBlock()}) diff --git a/internal/praefect/grpc-proxy/proxy/testhelper_test.go b/internal/praefect/grpc-proxy/proxy/testhelper_test.go index bc7ffca5e..4a8dd5aa0 100644 --- a/internal/praefect/grpc-proxy/proxy/testhelper_test.go +++ b/internal/praefect/grpc-proxy/proxy/testhelper_test.go @@ -41,7 +41,7 @@ func newBackendPinger(tb testing.TB, ctx context.Context) (*grpc.ClientConn, *in done := make(chan struct{}) go func() { defer close(done) - srvr.Serve(listener) + require.NoError(tb, srvr.Serve(listener)) }() cc, err := grpc.DialContext( @@ -83,7 +83,7 @@ func newProxy(tb testing.TB, ctx context.Context, director proxy.StreamDirector, listener := newListener(tb) go func() { defer close(done) - proxySrvr.Serve(listener) + require.NoError(tb, proxySrvr.Serve(listener)) }() proxyCC, err := grpc.DialContext( diff --git a/internal/praefect/middleware/errorhandler_test.go b/internal/praefect/middleware/errorhandler_test.go index 9e221e741..e87015a6c 100644 --- a/internal/praefect/middleware/errorhandler_test.go +++ b/internal/praefect/middleware/errorhandler_test.go @@ -64,7 +64,7 @@ func TestStreamInterceptor(t *testing.T) { mock.RegisterSimpleServiceServer(internalSrv, &simpleService{}) - go internalSrv.Serve(lis) + go testhelper.MustServe(t, internalSrv, lis) defer internalSrv.Stop() srvOptions := []grpc.ServerOption{ @@ -92,7 +92,7 @@ func TestStreamInterceptor(t *testing.T) { praefectSrv := grpc.NewServer(srvOptions...) defer praefectSrv.Stop() - go praefectSrv.Serve(praefectLis) + go testhelper.MustServe(t, praefectSrv, praefectLis) praefectCC, err := grpc.Dial("unix://"+praefectSocket, grpc.WithTransportCredentials(insecure.NewCredentials())) defer testhelper.MustClose(t, praefectCC) diff --git a/internal/praefect/node_test.go b/internal/praefect/node_test.go index b9a4bfecc..452a13d1a 100644 --- a/internal/praefect/node_test.go +++ b/internal/praefect/node_test.go @@ -54,7 +54,7 @@ func TestDialNodes(t *testing.T) { srv := grpc.NewServer() grpc_health_v1.RegisterHealthServer(srv, healthSrv) defer srv.Stop() - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) cfgNodes = append(cfgNodes, &config.Node{ Storage: n.storage, diff --git a/internal/praefect/nodes/sql_elector_test.go b/internal/praefect/nodes/sql_elector_test.go index b2c90ac21..adc79cb75 100644 --- a/internal/praefect/nodes/sql_elector_test.go +++ b/internal/praefect/nodes/sql_elector_test.go @@ -453,7 +453,7 @@ func TestConnectionMultiplexing(t *testing.T) { ln, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) db := testdb.New(t) mgr, err := NewManager( diff --git a/internal/praefect/remove_repository_test.go b/internal/praefect/remove_repository_test.go index f9eaf057a..4d8917378 100644 --- a/internal/praefect/remove_repository_test.go +++ b/internal/praefect/remove_repository_test.go @@ -124,7 +124,7 @@ func TestRemoveRepositoryHandler(t *testing.T) { ) defer srv.Stop() - go func() { srv.Serve(ln) }() + go testhelper.MustServe(t, srv, ln) clientConn, err := grpc.DialContext(ctx, "unix:"+ln.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) diff --git a/internal/praefect/replicator_pg_test.go b/internal/praefect/replicator_pg_test.go index 4bd66365e..2ee5ef568 100644 --- a/internal/praefect/replicator_pg_test.go +++ b/internal/praefect/replicator_pg_test.go @@ -45,7 +45,7 @@ func TestReplicatorInvalidSourceRepository(t *testing.T) { }, }) defer srv.Stop() - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) targetCC, err := client.Dial(ln.Addr().Network()+":"+ln.Addr().String(), nil) require.NoError(t, err) @@ -101,7 +101,7 @@ func TestReplicatorDestroy(t *testing.T) { return stream.SendMsg(&gitalypb.RemoveRepositoryResponse{}) })) - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) defer srv.Stop() clientConn, err := grpc.Dial(ln.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) diff --git a/internal/praefect/repository_exists_test.go b/internal/praefect/repository_exists_test.go index 3eb445e7e..ba005cc84 100644 --- a/internal/praefect/repository_exists_test.go +++ b/internal/praefect/repository_exists_test.go @@ -103,7 +103,7 @@ func TestRepositoryExistsHandler(t *testing.T) { ) defer srv.Stop() - go func() { srv.Serve(ln) }() + go testhelper.MustServe(t, srv, ln) clientConn, err := grpc.DialContext(ctx, "unix://"+ln.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) diff --git a/internal/praefect/server_factory_test.go b/internal/praefect/server_factory_test.go index 7fd850e21..bd3147d02 100644 --- a/internal/praefect/server_factory_test.go +++ b/internal/praefect/server_factory_test.go @@ -165,7 +165,7 @@ func TestServerFactory(t *testing.T) { return nil }) - defer waiter.Close() + defer testhelper.MustClose(t, waiter) _, err = gitalypb.NewSmartHTTPServiceClient(cc).PostUploadPackWithSidechannel(ctx, &gitalypb.PostUploadPackWithSidechannelRequest{Repository: repo}, @@ -184,9 +184,8 @@ func TestServerFactory(t *testing.T) { listener, err := net.Listen(starter.TCP, "localhost:0") require.NoError(t, err) - defer func() { require.NoError(t, listener.Close()) }() - go praefectServerFactory.Serve(listener, false) + go func() { require.NoError(t, praefectServerFactory.Serve(listener, false)) }() praefectAddr, err := starter.ComposeEndpoint(listener.Addr().Network(), listener.Addr().String()) require.NoError(t, err) @@ -217,9 +216,8 @@ func TestServerFactory(t *testing.T) { listener, err := net.Listen(starter.TCP, "localhost:0") require.NoError(t, err) - defer func() { require.NoError(t, listener.Close()) }() - go praefectServerFactory.Serve(listener, true) + go func() { require.NoError(t, praefectServerFactory.Serve(listener, true)) }() ctx := testhelper.Context(t) certPool, err := x509.SystemCertPool() @@ -260,9 +258,8 @@ func TestServerFactory(t *testing.T) { // start with tcp address tcpListener, err := net.Listen(starter.TCP, "localhost:0") require.NoError(t, err) - defer tcpListener.Close() - go praefectServerFactory.Serve(tcpListener, false) + go func() { require.NoError(t, praefectServerFactory.Serve(tcpListener, false)) }() praefectTCPAddr, err := starter.ComposeEndpoint(tcpListener.Addr().Network(), tcpListener.Addr().String()) require.NoError(t, err) @@ -276,9 +273,8 @@ func TestServerFactory(t *testing.T) { // start with tls address tlsListener, err := net.Listen(starter.TCP, "localhost:0") require.NoError(t, err) - defer tlsListener.Close() - go praefectServerFactory.Serve(tlsListener, true) + go func() { require.NoError(t, praefectServerFactory.Serve(tlsListener, true)) }() praefectTLSAddr, err := starter.ComposeEndpoint(tcpListener.Addr().Network(), tcpListener.Addr().String()) require.NoError(t, err) @@ -294,9 +290,8 @@ func TestServerFactory(t *testing.T) { defer func() { require.NoError(t, os.RemoveAll(socketPath)) }() socketListener, err := net.Listen(starter.Unix, socketPath) require.NoError(t, err) - defer socketListener.Close() - go praefectServerFactory.Serve(socketListener, false) + go func() { require.NoError(t, praefectServerFactory.Serve(socketListener, false)) }() praefectSocketAddr, err := starter.ComposeEndpoint(socketListener.Addr().Network(), socketListener.Addr().String()) require.NoError(t, err) diff --git a/internal/praefect/server_test.go b/internal/praefect/server_test.go index edbed63e0..15c69e35a 100644 --- a/internal/praefect/server_test.go +++ b/internal/praefect/server_test.go @@ -94,7 +94,7 @@ func TestNewBackchannelServerFactory(t *testing.T) { ln, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) - go server.Serve(ln) + go testhelper.MustServe(t, server, ln) ctx := testhelper.Context(t) nodeSet, err := DialNodes(ctx, []*config.VirtualStorage{{ @@ -848,7 +848,7 @@ func TestProxyWrites(t *testing.T) { listener, err := net.Listen("unix", socket) require.NoError(t, err) - go server.Serve(listener) + go testhelper.MustServe(t, server, listener) defer server.Stop() client, _ := newSmartHTTPClient(t, "unix://"+socket) @@ -1002,7 +1002,7 @@ func TestErrorThreshold(t *testing.T) { listener, err := net.Listen("unix", socket) require.NoError(t, err) - go server.Serve(listener) + go testhelper.MustServe(t, server, listener) defer server.Stop() conn, err := dial("unix://"+socket, []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}) diff --git a/internal/sidechannel/proxy.go b/internal/sidechannel/proxy.go index 6da61274f..ee92baebf 100644 --- a/internal/sidechannel/proxy.go +++ b/internal/sidechannel/proxy.go @@ -18,14 +18,19 @@ func NewUnaryProxy(registry *Registry) grpc.UnaryClientInterceptor { } ctx, waiter := RegisterSidechannel(ctx, registry, proxy(ctx)) - defer waiter.Close() + defer func() { + // We aleady check the error further down. + _ = waiter.Close() + }() if err := invoker(ctx, method, req, reply, cc, opts...); err != nil { return err } + if err := waiter.Close(); err != nil && err != ErrCallbackDidNotRun { return fmt.Errorf("sidechannel: proxy callback: %w", err) } + return nil } } diff --git a/internal/sidechannel/proxy_test.go b/internal/sidechannel/proxy_test.go index ed2f3e2a8..398d9c780 100644 --- a/internal/sidechannel/proxy_test.go +++ b/internal/sidechannel/proxy_test.go @@ -169,7 +169,7 @@ func testStreamProxy(t *testing.T, closeWrite bool) { conn, registry := dial(t, proxyAddr) ctx, waiter := RegisterSidechannel(ctx, registry, testProxyClient(closeWrite)) - defer waiter.Close() + defer testhelper.MustClose(t, waiter) client, err := gitalypb.NewSSHServiceClient(conn).SSHUploadPack(ctx) require.NoError(t, err) @@ -206,6 +206,6 @@ func startStreamServer(t *testing.T, handler func(gitalypb.SSHService_SSHUploadP require.NoError(t, err) t.Cleanup(srv.Stop) - go srv.Serve(ln) + go testhelper.MustServe(t, srv, ln) return ln.Addr().String() } diff --git a/internal/sidechannel/registry_test.go b/internal/sidechannel/registry_test.go index 8a286b617..d1a37b927 100644 --- a/internal/sidechannel/registry_test.go +++ b/internal/sidechannel/registry_test.go @@ -13,6 +13,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper" ) func TestRegistry(t *testing.T) { @@ -25,7 +26,7 @@ func TestRegistry(t *testing.T) { <-triggerCallback return nil }) - defer waiter.Close() + defer testhelper.MustClose(t, waiter) require.Equal(t, 1, registry.waiting()) @@ -57,7 +58,7 @@ func TestRegistry(t *testing.T) { return conn.CloseWrite() }) - defer waiter.Close() + defer testhelper.MustClose(t, waiter) require.NoError(t, registry.receive(waiter.id, client)) require.NoError(t, waiter.Close()) diff --git a/internal/sidechannel/sidechannel_test.go b/internal/sidechannel/sidechannel_test.go index 7043e1642..b07031323 100644 --- a/internal/sidechannel/sidechannel_test.go +++ b/internal/sidechannel/sidechannel_test.go @@ -157,16 +157,15 @@ func startServer(t *testing.T, th testHandler, opts ...grpc.ServerOption) string opts = append(opts, grpc.Creds(lm)) s := grpc.NewServer(opts...) - t.Cleanup(func() { s.Stop() }) + t.Cleanup(s.Stop) handler := &server{testHandler: th} healthpb.RegisterHealthServer(s, handler) lis, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) - t.Cleanup(func() { lis.Close() }) - go func() { s.Serve(lis) }() + go testhelper.MustServe(t, s, lis) return lis.Addr().String() } @@ -187,7 +186,10 @@ func call(ctx context.Context, conn *grpc.ClientConn, registry *Registry, handle client := healthpb.NewHealthClient(conn) ctxOut, waiter := RegisterSidechannel(ctx, registry, handler) - defer waiter.Close() + defer func() { + // We aleady check the error further down. + _ = waiter.Close() + }() if _, err := client.Check(ctxOut, &healthpb.HealthCheckRequest{}); err != nil { return err diff --git a/internal/testhelper/testhelper.go b/internal/testhelper/testhelper.go index 44f691452..02440e7f0 100644 --- a/internal/testhelper/testhelper.go +++ b/internal/testhelper/testhelper.go @@ -8,11 +8,13 @@ import ( "crypto/rand" "crypto/x509" "encoding/pem" + "errors" "fmt" "io" "math/big" mrand "math/rand" "net" + "net/http" "os" "os/exec" "path/filepath" @@ -102,6 +104,25 @@ func MustClose(tb testing.TB, closer io.Closer) { require.NoError(tb, closer.Close()) } +// Server is an interface for a server that can serve requests on a specific listener. This +// interface is used by the MustServe helper function. +type Server interface { + Serve(net.Listener) error +} + +// MustServe starts to serve the given server with the listener. This function asserts that the +// server was able to successfully serve and is useful in contexts where one wants to simply spawn a +// server in a Goroutine. +func MustServe(tb testing.TB, server Server, listener net.Listener) { + tb.Helper() + + // `http.Server.Serve()` is expected to return `http.ErrServerClosed`, so we special-case + // this error here. + if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { + require.NoError(tb, err) + } +} + // CopyFile copies a file at the path src to a file at the path dst func CopyFile(tb testing.TB, src, dst string) { fsrc, err := os.Open(src) diff --git a/packed_binaries.go b/packed_binaries.go index fe3e435e0..6b0774988 100644 --- a/packed_binaries.go +++ b/packed_binaries.go @@ -40,14 +40,20 @@ func UnpackAuxiliaryBinaries(destinationDir string) error { if err != nil { return fmt.Errorf("open packed binary %q: %w", packedPath, err) } - defer packedFile.Close() + defer func() { + // We already check the error below. + _ = packedFile.Close() + }() unpackedPath := filepath.Join(destinationDir, entry.Name()) unpackedFile, err := os.OpenFile(unpackedPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o700) if err != nil { return err } - defer unpackedFile.Close() + defer func() { + // We already check the error below. + _ = unpackedFile.Close() + }() if _, err := io.Copy(unpackedFile, packedFile); err != nil { return fmt.Errorf("unpack %q: %w", unpackedPath, err) @@ -57,6 +63,10 @@ func UnpackAuxiliaryBinaries(destinationDir string) error { return fmt.Errorf("close %q: %w", unpackedPath, err) } + if err := packedFile.Close(); err != nil { + return fmt.Errorf("close packed file %q: %w", packedPath, err) + } + return nil }(); err != nil { return err |