Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue with conflicting ports when starting multiple Dev sessions on Podman #6660

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this change be reverted once the regression has been fixed? If so, can you add a todo about it?

Copy link
Member Author

@rm3l rm3l Mar 14, 2023

Choose a reason for hiding this comment

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

To me, this is still valid even after the regression is fixed in the kernel. Podman binds by default to 0.0.0.0, so it makes sense to check on all addresses. As for Kubernetes, port-forwarding currently binds to 127.0.0.1 by default, but it makes sense to me to still check on all addresses.
As part of #6479, we would need to properly control the address on which to bind and pass the right address to this function.

listener, err := net.Listen("tcp", address)
if err != nil {
return false
}
_ = listener.Addr().(*net.TCPAddr).Port
Copy link
Contributor

Choose a reason for hiding this comment

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

What did this do?

Copy link
Member Author

@rm3l rm3l Mar 14, 2023

Choose a reason for hiding this comment

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

It can be used to determine the port value on which we are listening (by casting listener.Addr() into *net.TCPAddr). But the return value is not used at all. And it is useless to me since we are already passing the input port to net.Listen. It would have been useful for example if we were letting the kernel bind to a random port and we wanted to get that random port value.

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)
}
})
}

}