Skip to content

Commit 3f6193f

Browse files
authored
feat(board): remove random port selection (#698)
1 parent c38fef3 commit 3f6193f

File tree

6 files changed

+49
-45
lines changed

6 files changed

+49
-45
lines changed

pkg/board/remote/adb/adb.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"github.com/arduino/go-paths-helper"
1919

2020
"github.com/bcmi-labs/orchestrator/pkg/board/remote"
21-
"github.com/bcmi-labs/orchestrator/pkg/board/remote/common"
21+
"github.com/bcmi-labs/orchestrator/pkg/x/ports"
2222
)
2323

2424
const username = "arduino"
@@ -56,27 +56,27 @@ func FromHost(host string, adbPath string) (*ADBConnection, error) {
5656
return FromSerial(host, adbPath)
5757
}
5858

59-
func (a *ADBConnection) Forward(ctx context.Context, remotePort int) (int, error) {
60-
hostAvailablePort, err := common.GetAvailablePort()
61-
if err != nil {
62-
return 0, fmt.Errorf("failed to find an available port: %w", err)
59+
func (a *ADBConnection) Forward(ctx context.Context, localPort int, remotePort int) error {
60+
if !ports.IsAvailable(localPort) {
61+
return remote.ErrPortAvailable
6362
}
6463

65-
local := fmt.Sprintf("tcp:%d", hostAvailablePort)
64+
local := fmt.Sprintf("tcp:%d", localPort)
6665
remote := fmt.Sprintf("tcp:%d", remotePort)
6766
cmd, err := paths.NewProcess(nil, a.adbPath, "-s", a.host, "forward", local, remote)
6867
if err != nil {
69-
return hostAvailablePort, err
68+
return err
7069
}
7170
if err := cmd.RunWithinContext(ctx); err != nil {
72-
return hostAvailablePort, fmt.Errorf(
71+
return fmt.Errorf(
7372
"failed to forward ADB port %s to %s: %w",
7473
local,
7574
remote,
7675
err,
7776
)
7877
}
79-
return hostAvailablePort, nil
78+
79+
return nil
8080
}
8181

8282
func (a *ADBConnection) ForwardKillAll(ctx context.Context) error {

pkg/board/remote/local/local.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ type LocalConnection struct{}
1818
// Ensures LocalConnection implements the RemoteConn interface at compile time.
1919
var _ remote.RemoteConn = (*LocalConnection)(nil)
2020

21-
func (a *LocalConnection) Forward(ctx context.Context, remotePort int) (int, error) {
21+
func (a *LocalConnection) Forward(ctx context.Context, localPort int, remotePort int) error {
2222
// Locally we don't need to forward ports.
23-
return remotePort, nil
23+
return nil
2424
}
2525

2626
func (a *LocalConnection) ForwardKillAll(ctx context.Context) error {

pkg/board/remote/remote.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package remote
22

33
import (
44
"context"
5+
"fmt"
56
"io"
67
)
78

9+
var ErrPortAvailable = fmt.Errorf("port is not available")
10+
811
type FileInfo struct {
912
Name string
1013
IsDir bool
@@ -30,7 +33,7 @@ type RemoteShell interface {
3033
}
3134

3235
type Forwarder interface {
33-
Forward(ctx context.Context, remotePort int) (int, error)
36+
Forward(ctx context.Context, localPort int, remotePort int) error
3437
ForwardKillAll(ctx context.Context) error
3538
}
3639

pkg/board/remote/remote_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/bcmi-labs/orchestrator/pkg/board/remote/adb"
1919
"github.com/bcmi-labs/orchestrator/pkg/board/remote/local"
2020
"github.com/bcmi-labs/orchestrator/pkg/board/remote/ssh"
21+
"github.com/bcmi-labs/orchestrator/pkg/x/ports"
2122
"github.com/bcmi-labs/orchestrator/pkg/x/testtools"
2223
)
2324

@@ -182,7 +183,10 @@ func TestSSHForwarder(t *testing.T) {
182183
ctx, cancel := context.WithCancel(t.Context())
183184
defer cancel()
184185

185-
forwardPort, err := conn.Forward(ctx, 5555)
186+
forwardPort, err := ports.GetAvailable()
187+
require.NoError(t, err)
188+
189+
err = conn.Forward(ctx, forwardPort, 5555)
186190
if err != nil {
187191
t.Errorf("Forward failed: %v", err)
188192
}
@@ -213,7 +217,10 @@ func TestSSHKillForwarder(t *testing.T) {
213217
ctx, cancel := context.WithCancel(t.Context())
214218
defer cancel()
215219

216-
forwardPort, err := conn.Forward(ctx, 5555)
220+
forwardPort, err := ports.GetAvailable()
221+
require.NoError(t, err)
222+
223+
err = conn.Forward(ctx, forwardPort, 5555)
217224
if err != nil {
218225
t.Errorf("Forward failed: %v", err)
219226
}

pkg/board/remote/ssh/ssh.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"io"
99
"io/fs"
10-
"log"
1110
"log/slog"
1211
"net"
1312
"path"
@@ -17,7 +16,7 @@ import (
1716
"golang.org/x/crypto/ssh"
1817

1918
"github.com/bcmi-labs/orchestrator/pkg/board/remote"
20-
"github.com/bcmi-labs/orchestrator/pkg/board/remote/common"
19+
"github.com/bcmi-labs/orchestrator/pkg/x/ports"
2120
)
2221

2322
var ErrAuthFailed = errors.New("ssh authentication failed")
@@ -57,18 +56,14 @@ func FromHost(user, password, address string) (*SSHConnection, error) {
5756
}, nil
5857
}
5958

60-
func (a *SSHConnection) Forward(ctx context.Context, remotePort int) (int, error) {
61-
62-
// Get a random available port for remote connection
63-
randomPort, err := common.GetAvailablePort()
64-
if err != nil {
65-
log.Printf("failed to get available port: %v", err)
66-
return 0, err
59+
func (a *SSHConnection) Forward(ctx context.Context, localPort int, remotePort int) error {
60+
if !ports.IsAvailable(localPort) {
61+
return remote.ErrPortAvailable
6762
}
68-
// Listen locally on remote port
69-
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "localhost", randomPort))
63+
64+
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "localhost", localPort))
7065
if err != nil {
71-
return 0, err
66+
return err
7267
}
7368

7469
a.mu.Lock()
@@ -112,8 +107,7 @@ func (a *SSHConnection) Forward(ctx context.Context, remotePort int) (int, error
112107
}
113108
}()
114109

115-
return randomPort, nil
116-
110+
return nil
117111
}
118112

119113
func copyAndLog(dst io.Writer, src io.Reader) {
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,14 @@
1-
package common
1+
package ports
22

33
import (
44
"fmt"
55
"math/rand/v2"
66
"net"
77
)
88

9-
func isPortAvailable(port int) bool {
10-
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
11-
if err != nil {
12-
return false
13-
}
14-
listener.Close()
15-
return true
16-
}
17-
18-
func getRandomPort() int {
19-
port := 1000 + rand.IntN(9000) // nolint:gosec
20-
return port
21-
}
22-
239
const forwardPortAttempts = 10
2410

25-
func GetAvailablePort() (int, error) {
11+
func GetAvailable() (int, error) {
2612
tried := make(map[int]any, forwardPortAttempts)
2713
for len(tried) < forwardPortAttempts {
2814
port := getRandomPort()
@@ -31,9 +17,23 @@ func GetAvailablePort() (int, error) {
3117
}
3218
tried[port] = struct{}{}
3319

34-
if isPortAvailable(port) {
20+
if IsAvailable(port) {
3521
return port, nil
3622
}
3723
}
3824
return 0, fmt.Errorf("no available port found in range 1000-9999 after %d attempts", forwardPortAttempts)
3925
}
26+
27+
func IsAvailable(port int) bool {
28+
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
29+
if err != nil {
30+
return false
31+
}
32+
listener.Close()
33+
return true
34+
}
35+
36+
func getRandomPort() int {
37+
port := 1000 + rand.IntN(9000) // nolint:gosec
38+
return port
39+
}

0 commit comments

Comments
 (0)