Skip to content

Conversation

@thaJeztah
Copy link
Member

@thaJeztah thaJeztah commented Feb 12, 2024

relates to:

cli-plugins/socket: remove use of deprecated distribution uuid package

The "github.com/docker/distribution" module moved to the distribution
org ("github.com/docker/distribution/v3"), and the new module deprecated
and removed the uuid package in favor of Google's UUID package.

While we still depend on the old module through packages and as an indirect
dependency, we may want to try avoid using it.

This patch replaces the use for the socket package, and replaces it for a
local utility, taking the same approach as stringid.GenerateRandomID(),
which should be random enough for this purpose.

- Description for the changelog

- A picture of a cute animal (not mandatory but encouraged)

@thaJeztah thaJeztah added status/2-code-review kind/refactor PR's that refactor, or clean-up code labels Feb 12, 2024
@thaJeztah thaJeztah added this to the 26.0.0 milestone Feb 12, 2024
@thaJeztah thaJeztah self-assigned this Feb 12, 2024
@thaJeztah thaJeztah force-pushed the use_non_deprecated_uuid branch from ebd60c0 to 6a18d84 Compare February 12, 2024 12:54
@codecov-commenter
Copy link

codecov-commenter commented Feb 12, 2024

Codecov Report

Merging #4872 (3b5e814) into master (9831fea) will increase coverage by 0.00%.
Report is 1 commits behind head on master.
The diff coverage is 66.66%.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4872   +/-   ##
=======================================
  Coverage   61.31%   61.32%           
=======================================
  Files         287      287           
  Lines       20058    20063    +5     
=======================================
+ Hits        12298    12303    +5     
  Misses       6867     6867           
  Partials      893      893           

@thaJeztah
Copy link
Member Author

@krissetto @laurazard ptal 🤗

@thaJeztah thaJeztah force-pushed the use_non_deprecated_uuid branch from 6a18d84 to f4fff92 Compare February 13, 2024 13:06
@thaJeztah thaJeztah changed the title cli-plugins/socket: use google's uuid package instead of distribution cli-plugins/socket: use stringid instead of distribution's uuid package Feb 13, 2024
@thaJeztah
Copy link
Member Author

Updated; I'll squash later if we all agree 👍

Copy link
Member

@Benehiko Benehiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could add a test to prove collisions aren't possible for certain number of calls? I know it uses crypto/rand, so it shouldn't have a problem, but might be good to have a test so that this behavior doesn't change in future.

Other than that it LGTM :)

@thaJeztah
Copy link
Member Author

Pushed another option to just use a local utility, based on the stringid approach, but without the check if it's a numeric value, as that's not needed here.

@thaJeztah
Copy link
Member Author

(I'lll squash commits if we agree, so don't merge yet ⚠️)

@laurazard
Copy link
Collaborator

I'm happy with us defining a local utility for this!

Performance shouldn't be a huge concern as long as we don't start taking multiple milliseconds since this should generally only happen once per plugin invocation, and randomness isn't worrying either (it really only serves to let us come up with something that hopefully doesn't collide with any other currently opened socket).

Copy link
Collaborator

@vvoland vvoland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM; I really wouldn't worry about collisions here. It's 32 bytes (2^256) - I'm pretty sure it won't be a problem on a properly working system for any human being ever 😄

@thaJeztah thaJeztah force-pushed the use_non_deprecated_uuid branch from ea173d3 to 2e1478c Compare February 13, 2024 14:49
@thaJeztah thaJeztah changed the title cli-plugins/socket: use stringid instead of distribution's uuid package cli-plugins/socket: remove use of deprecated distribution uuid package Feb 13, 2024
@thaJeztah
Copy link
Member Author

Thanks all! I squashed the commits; we can merge once CI finishes

Copy link
Collaborator

@vvoland vvoland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I missed the failure:

- FAIL: TestSetupConn (0.00s)
    --- FAIL: TestSetupConn/updates_conn_when_connected (0.00s)
        socket_test.go:20: assertion failed: error is not nil: listen unix /var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_44b21e358063d1d8813046be2cb6c105391492639decad3d90af0f5fd193f090: bind: invalid argument
    --- FAIL: TestSetupConn/allows_reconnects (0.00s)
        socket_test.go:34: assertion failed: error is not nil: listen unix /var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_25007de380ec8be00ed832285a3304c4170b547c2d87f4431145867e23f5bb52: bind: invalid argument
    --- FAIL: TestSetupConn/does_not_leak_sockets_to_local_directory (0.00s)
        socket_test.go:51: assertion failed: error is not nil: listen unix /var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_ac4e8a31d2b46b186de29d235c13f52cecca70a2f8fa23e85c3f0e227a67f5ae: bind: invalid argument
--- FAIL: TestConnectAndWait (0.00s)
    --- FAIL: TestConnectAndWait/calls_cancel_func_on_EOF (0.00s)
        socket_test.go:83: assertion failed: error is not nil: listen unix /var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_ae5b44d10a06f5d2bef1e114be4c0bf1bc94426d9c72c8694a1b97505497ed10: bind: invalid argument: failed to setup listener
    --- FAIL: TestConnectAndWait/connect_goroutine_exits_after_EOF (0.00s)
        socket_test.go:106: assertion failed: error is not nil: listen unix /var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_bc4bec58d28c315fd4558146986a442e99262fe7f9d381e7bc56345d88621dbb: bind: invalid argument: failed to setup listener
FAIL

I think the filename is too long now

@thaJeztah
Copy link
Member Author

Oh! Probably yes. I also missed that it failed 😞

Let me have a look

The "github.com/docker/distribution" module moved to the distribution
org ("github.com/docker/distribution/v3"), and the new module deprecated
and removed the uuid package in favor of Google's UUID package.

While we still depend on the old module through packages and as an indirect
dependency, we may want to try avoid using it.

This patch replaces the use for the socket package, and replaces it for a
local utility, taking the same approach as `stringid.GenerateRandomID()`,
which should be random enough for this purpose.

Signed-off-by: Sebastiaan van Stijn <[email protected]>
@thaJeztah thaJeztah force-pushed the use_non_deprecated_uuid branch from 2e1478c to 3b5e814 Compare February 13, 2024 15:25
}

func randomID() string {
b := make([]byte, 16)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to 16 bytes; which is the same as the UUID (output is slightly shorter as it doesn't add hyphen in between;

before: 332becff-7147-44cc-ac72-8ab20eac060e
after:  68298c4c32a19c2b1cc636649c465d84

@thaJeztah
Copy link
Member Author

Hm... one more failure; what's this?

--- FAIL: TestConnectAndWait (0.00s)
    --- FAIL: TestConnectAndWait/connect_goroutine_exits_after_EOF (0.00s)
        socket_test.go:111: assertion failed: 9 (int) != 10 (numGoroutines + 1 int)

@thaJeztah
Copy link
Member Author

Hmm.. other failure now. I have a feeling we may have some flakiness (don't immediately see how this PR would change anything there 🤔)

--- FAIL: TestSetupConn (0.00s)
    --- FAIL: TestSetupConn/allows_reconnects (0.00s)
        socket_test.go:45: assertion failed: error is not nil: dial unix /var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_acd932c061c5c7027bf7bd6bf1aa7be5: connect: no such file or directory: failed to redial listener

@laurazard
Copy link
Collaborator

Oh, that's weird. I wonder if we broke something, I don't think the allows reconnects test should be flaky – it's a straightforward "we can reconnect to this socket test". Maybe someone can take a look?

@thaJeztah
Copy link
Member Author

Yeah, that one struck me as weird as well. At least it's not the path length this time (/var/folders/gh/0cww6vn93nv_jjsw3xkm0b4m0000gn/T/docker_cli_acd932c061c5c7027bf7bd6bf1aa7be5 is 92 chars, so should not be an issue),

That said, I know the macOS check is a bit flaky on its own (tends to fail on ctn, but always passes on the second try), so it's still possible something lead to that failure, not the test itself. And of course there's macOS's magic temp-dirs, which have been fun at times due to their symlinks, but don't think that's at play here either.

Looks like race-detector found some issues, but haven't looked at them yet output below;

go test -v . -race
=== RUN   TestSetupConn
=== RUN   TestSetupConn/updates_conn_when_connected
==================
WARNING: DATA RACE
Write at 0x00c000124078 by goroutine 9:
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x64

Previous read at 0x00c000124078 by goroutine 10:
  github.com/docker/cli/cli-plugins/socket.pollConnNotNil.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:128 +0x30
  gotest.tools/v3/poll.WaitOn.func1()
      /Users/thajeztah/Projects/cli/vendor/gotest.tools/v3/poll/poll.go:125 +0x60

Goroutine 9 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestSetupConn.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:19 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 10 (finished) created at:
  gotest.tools/v3/poll.WaitOn()
      /Users/thajeztah/Projects/cli/vendor/gotest.tools/v3/poll/poll.go:124 +0x220
  github.com/docker/cli/cli-plugins/socket.pollConnNotNil()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:127 +0x134
  github.com/docker/cli/cli-plugins/socket.TestSetupConn.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:28 +0x26c
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
    testing.go:1465: race detected during execution of test
=== RUN   TestSetupConn/allows_reconnects
=== RUN   TestSetupConn/does_not_leak_sockets_to_local_directory
=== NAME  TestSetupConn
    testing.go:1465: race detected during execution of test
--- FAIL: TestSetupConn (0.00s)
    --- FAIL: TestSetupConn/updates_conn_when_connected (0.00s)
    --- PASS: TestSetupConn/allows_reconnects (0.00s)
    --- PASS: TestSetupConn/does_not_leak_sockets_to_local_directory (0.00s)
=== RUN   TestConnectAndWait
=== RUN   TestConnectAndWait/calls_cancel_func_on_EOF
==================
WARNING: DATA RACE
Read at 0x00c000206018 by goroutine 18:
  net.(*conn).ok()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:170 +0x2c
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:200 +0x38
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:92 +0x1ac
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000206018 by goroutine 19:
  net.newUnixConn()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:195 +0x54
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:176 +0x8c
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 18 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:80 +0x3c
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 19 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:82 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c000200500 by goroutine 18:
  ??()
      -:0 +0x1040f1ebc
  sync/atomic.LoadUint64()
      <autogenerated>:1 +0x10
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:91 +0x2c
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:92 +0x1ac
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000200500 by goroutine 19:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 18 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:80 +0x3c
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 19 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:82 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c000200520 by goroutine 18:
  internal/poll.(*pollDesc).evict()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_poll_runtime.go:58 +0x44
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:100 +0x40
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:92 +0x1ac
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000200520 by goroutine 19:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 18 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:80 +0x3c
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 19 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:82 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c000200510 by goroutine 18:
  internal/poll.(*FD).destroy()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:81 +0x70
  internal/poll.(*FD).decref()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_mutex.go:213 +0x38
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:104 +0x6c
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:92 +0x1ac
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000200510 by goroutine 19:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 18 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:80 +0x3c
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 19 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:82 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c00020052c by goroutine 18:
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:112 +0x84
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:92 +0x1ac
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000200528 by goroutine 19:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 18 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:80 +0x3c
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 19 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:82 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
    testing.go:1465: race detected during execution of test
=== RUN   TestConnectAndWait/connect_goroutine_exits_after_EOF
==================
WARNING: DATA RACE
Read at 0x00c000124088 by goroutine 22:
  net.(*conn).ok()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:170 +0x2c
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:200 +0x38
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:114 +0x190
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000124088 by goroutine 23:
  net.newUnixConn()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:195 +0x54
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:176 +0x8c
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 22 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:103 +0x58
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 23 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:105 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c000168580 by goroutine 22:
  ??()
      -:0 +0x1040f1ebc
  sync/atomic.LoadUint64()
      <autogenerated>:1 +0x10
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:91 +0x2c
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:114 +0x190
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000168580 by goroutine 23:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 22 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:103 +0x58
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 23 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:105 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c0001685a0 by goroutine 22:
  internal/poll.(*pollDesc).evict()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_poll_runtime.go:58 +0x44
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:100 +0x40
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:114 +0x190
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c0001685a0 by goroutine 23:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 22 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:103 +0x58
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 23 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:105 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c000168590 by goroutine 22:
  internal/poll.(*FD).destroy()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:81 +0x70
  internal/poll.(*FD).decref()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_mutex.go:213 +0x38
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:104 +0x6c
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:114 +0x190
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c000168590 by goroutine 23:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 22 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:103 +0x58
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 23 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:105 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
==================
WARNING: DATA RACE
Read at 0x00c0001685ac by goroutine 22:
  internal/poll.(*FD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/internal/poll/fd_unix.go:112 +0x84
  net.(*netFD).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_posix.go:37 +0x44
  net.(*conn).Close()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/net.go:203 +0x68
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:114 +0x190
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Previous write at 0x00c0001685a8 by goroutine 23:
  net.newFD()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:27 +0x1dc
  net.(*netFD).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/fd_unix.go:180 +0x158
  net.(*UnixListener).accept()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock_posix.go:172 +0x38
  net.(*UnixListener).AcceptUnix()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/net/unixsock.go:247 +0x60
  github.com/docker/cli/cli-plugins/socket.accept.func1()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:43 +0x50

Goroutine 22 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x5e8
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:103 +0x58
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40

Goroutine 23 (running) created at:
  github.com/docker/cli/cli-plugins/socket.accept()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:39 +0xc4
  github.com/docker/cli/cli-plugins/socket.SetupConn()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket.go:25 +0x78
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func2()
      /Users/thajeztah/Projects/cli/cli-plugins/socket/socket_test.go:105 +0x64
  testing.tRunner()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1595 +0x1b0
  testing.(*T).Run.func1()
      /opt/homebrew/Cellar/go/1.21.6/libexec/src/testing/testing.go:1648 +0x40
==================
    testing.go:1465: race detected during execution of test
=== NAME  TestConnectAndWait
    testing.go:1465: race detected during execution of test
--- FAIL: TestConnectAndWait (0.00s)
    --- FAIL: TestConnectAndWait/calls_cancel_func_on_EOF (0.00s)
    --- FAIL: TestConnectAndWait/connect_goroutine_exits_after_EOF (0.00s)
=== NAME
    testing.go:1465: race detected during execution of test
FAIL
FAIL	github.com/docker/cli/cli-plugins/socket	0.765s
FAIL

@vvoland
Copy link
Collaborator

vvoland commented Feb 14, 2024

Looks like we lack synchronization around the **net.UnixConn itself - we overwrite it in a separate accept goroutine:

func accept(listener *net.UnixListener, conn **net.UnixConn) {
go func() {
for {
// ignore error here, if we failed to accept a connection,
// conn is nil and we fallback to previous behavior
*conn, _ = listener.AcceptUnix()
// perform any platform-specific actions on accept (e.g. unlink non-abstract sockets)
onAccept(*conn, listener)
}
}()
}

and also access it in tests:

if *conn == nil {

It might be only a tests issue though, since the overwrite should happen only once because we only expect one connection to the socket?

Copy link
Collaborator

@vvoland vvoland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, it's definitely unrelated to this PR, so this one LGTM.

@thaJeztah
Copy link
Member Author

Looks like we lack synchronization around the **net.UnixConn itself - we overwrite it in a separate accept goroutine:

Yes, was looking at that code Yesterday, and wondering if that should terminate the loop if it returns an error. Looks like we currently never terminate the loop..

I just tried running these tests in a loop every 0.5 seconds for ~15 minutes (so ~ 1767 times), and didn't get a failure, so for now I blame some general flakiness for those above.

time watch -n 0.5 go test -test.shuffle=on -v .

________________________________________________________
Executed in  883.57 secs    fish           external
   usr time  274.13 secs   89.00 micros  274.13 secs
   sys time  331.88 secs  548.00 micros  331.88 secs

Let's bring this one in. But perhaps someone should have a look at the above things.

@thaJeztah thaJeztah merged commit 155b5b4 into docker:master Feb 14, 2024
@thaJeztah thaJeztah deleted the use_non_deprecated_uuid branch February 14, 2024 09:33
@laurazard
Copy link
Collaborator

I think originally the idea was to explicitly allow plugins to redial the socket (for whatever end), but that doesn't sound super important right now and we can introduce it later if we want to. I'm okay with either explicitly only allowing one connection (terminate the loop after error) or protect access to con.

cc @Benehiko if you're looking to dig in to something

@thaJeztah
Copy link
Member Author

Yeah, I wasn't 100% sure yet for the best approach there. If were successfully able to connect, it would block, but on any error it would fall through, and call the onAccept() code, which (depending on the platform) would either be a no-op (try again), or (on macOS) delete the socket, so now we'd end up in a loop trying to connect to a socket that's gone (and would never re-appear?). Perhaps we could still keep the re-connect behavior if we need, but add some error-handling to terminate depending on the type of error? (haven't looked closely into what errors can be returned yet).

@Benehiko
Copy link
Member

Benehiko commented Feb 15, 2024

TL;DR conn *net.UnixConn is not concurrency safe. #4878

The problem is that the onAccept() function is in an infinite loop, always setting the con variable to some value, either nil or UnixConn. Then later in the test we call the Close() method even though the goroutine is still being executed (setting the conn variable).

I don't have enough context of why the accept function needs to be an infinite loop inside a goroutine, but since we aren't exposing a channel back to the consumer of this function to say "conn is active" we could run into a case where conn has been set to nil or some value while it is being read. See below for some go code explaining this.

var conn *net.UnixConn
// sets up an inifinte loop in the background
listener, err := SetupConn(&conn)

// poll the conn for non nill 
pollConnNotNil(t, conn) // <-- can succeed or fail here

// closes the connection
conn.Close() // <-- can succeed or fail here
// Close closes the connection.
func (c *conn) Close() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	err := c.fd.Close() // <--- fails
	if err != nil {
		err = &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return err
}
go test -v -race .
---
WARNING: DATA RACE
Read at 0x00c00013322c by goroutine 16:
  internal/poll.(*FD).Close()
      /usr/lib/go/src/internal/poll/fd_unix.go:112 +0x98
  net.(*netFD).Close()
      /usr/lib/go/src/net/fd_posix.go:37 +0x3d
  net.(*conn).Close()
      /usr/lib/go/src/net/net.go:203 +0x6a
  github.com/docker/cli/cli-plugins/socket.TestConnectAndWait.func1()
      /home/benehiko/go/src/github.com/docker/cli/cli-plugins/socket/socket_test.go:107 +0x2dd
  testing.tRunner()
      /usr/lib/go/src/testing/testing.go:1595 +0x261
  testing.(*T).Run.func1()
      /usr/lib/go/src/testing/testing.go:1648 +0x44

Solutions:

  1. Proceed with infinite loop, but explicitly lock a mutex when setting the conn variable.
  2. Find an alternative to an infinite loop always setting conn, either exit the loop when not-nil or a channel returned to the caller.

A solution i can commit now is to wrap net.UnixConn to make it concurrent safe.

// UnixConnSafe is a wrapper around a UnixConn
// that makes it concurrency-safe.
type UnixConnSafe struct {
	*net.UnixConn
	SocketLock *sync.Mutex
}

func NewUnixConnSafe() *UnixConnSafe {
	return &UnixConnSafe{
		UnixConn:   &net.UnixConn{},
		SocketLock: &sync.Mutex{},
	}
}

func (c *UnixConnSafe) Close() error {
	c.SocketLock.Lock()
	defer c.SocketLock.Unlock()
	if c.UnixConn == nil {
		return nil
	}
	return c.UnixConn.Close()
}

func (c *UnixConnSafe) IsNil() bool {
	c.SocketLock.Lock()
	defer c.SocketLock.Unlock()
	return c.UnixConn == nil
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/plugins kind/refactor PR's that refactor, or clean-up code status/2-code-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants