Skip to content

Commit

Permalink
Fix issue with conflicting ports when starting multiple Dev sessions …
Browse files Browse the repository at this point in the history
…on Podman

Due to regression on 6.1+ kernels [1], binding on 127.0.0.1:$port after some process
listens on 0.0.0.0:$port does pass, which is not expected.
Until this issue is fixed [2], a possible workaround is to try to bind on 0.0.0.0:$port.
This works correctly on Podman, and also on Kubernetes
(because we do port-forwarding on Kubernetes on 127.0.0.1:$port).

The unit test added should allow us to detect similar issues faster in the future,
should it happen again.

[1] https://lore.kernel.org/stable/e21bf153-80b0-9ec0-15ba-e04a4ad42c34@redhat.com/T/
[2] https://lore.kernel.org/netdev/20230312031904.4674-3-kuniyu@amazon.com/T/
  • Loading branch information
rm3l committed Mar 13, 2023
1 parent 8a002e1 commit 452d1cc
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 2 deletions.
3 changes: 1 addition & 2 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,12 +782,11 @@ func SafeGetBool(b *bool) bool {

// IsPortFree checks if the port on localhost is free to use
func IsPortFree(port int) bool {
address := fmt.Sprintf("localhost:%d", port)
address := fmt.Sprintf("0.0.0.0:%d", port)
listener, err := net.Listen("tcp", address)
if err != nil {
return false
}
_ = listener.Addr().(*net.TCPAddr).Port
err = listener.Close()
return err == nil
}
Expand Down
126 changes: 126 additions & 0 deletions pkg/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2735,3 +2735,129 @@ bar
})
}
}

func TestIsPortFree(t *testing.T) {
type serverCloser interface {
Close()
}
type args struct {
port int
portProvider func() (int, serverCloser, error)
}
type test struct {
name string
args args
want bool
}
tests := []test{
{
name: "negative port should return an error, handles as false",
args: args{port: -10},
want: false,
},
{
name: "0 should always be free",
args: args{port: 0},
want: true,
},
{
name: "random port bound on 127.0.0.1",
args: args{
portProvider: func() (int, serverCloser, error) {
s := httptest.NewServer(nil)
_, p, err := net.SplitHostPort(strings.TrimPrefix(s.URL, "http://"))
if err != nil {
return 0, s, err
}
port, err := strconv.Atoi(p)
if err != nil {
return 0, s, err
}
return port, s, nil
},
},
want: false,
},
{
name: "random port bound on 127.0.0.1 and checking 0 as input",
args: args{
portProvider: func() (int, serverCloser, error) {
s := httptest.NewServer(nil)
return 0, s, nil
},
},
want: true,
},
{
name: "random port bound on 0.0.0.0 and checking 0 as input",
args: args{
portProvider: func() (int, serverCloser, error) {
// Intentionally not using httptest.Server, which listens to 127.0.0.1
l, err := net.Listen("tcp", "0.0.0.0:0")
if err != nil {
return 0, nil, err
}
s := &httptest.Server{
Listener: l,
Config: &http.Server{},
}
s.Start()

return 0, s, nil
},
},
want: true,
},
{
name: "random port bound on 0.0.0.0",
args: args{
portProvider: func() (int, serverCloser, error) {
// Intentionally not using httptest.Server, which listens to 127.0.0.1
l, err := net.Listen("tcp", "0.0.0.0:0")
if err != nil {
return 0, nil, err
}
s := &httptest.Server{
Listener: l,
Config: &http.Server{},
}
s.Start()

_, p, err := net.SplitHostPort(strings.TrimPrefix(s.URL, "http://"))
if err != nil {
return 0, s, err
}
port, err := strconv.Atoi(p)
if err != nil {
return 0, s, err
}
return port, s, nil
},
},
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
port := tt.args.port
var s serverCloser
var err error
if tt.args.portProvider != nil {
port, s, err = tt.args.portProvider()
if s != nil {
defer s.Close()
}
if err != nil {
t.Errorf("error while computing port: %v", err)
return
}
}

if got := IsPortFree(port); got != tt.want {
t.Errorf("IsPortFree() = %v, want %v", got, tt.want)
}
})
}

}

0 comments on commit 452d1cc

Please sign in to comment.