Skip to content

Commit acd183d

Browse files
committed
fix(envd): log net.Connections error in port scanner and add unit tests
1 parent 0716edb commit acd183d

5 files changed

Lines changed: 134 additions & 10 deletions

File tree

‎packages/envd/internal/port/forward_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func TestStartForwarding_StopsOnClosedMessages(t *testing.T) {
1717
t.Parallel()
1818

1919
l := zerolog.Nop()
20-
scanner := NewScanner(time.Hour)
20+
scanner := NewScanner(&l, time.Hour)
2121
f := &Forwarder{
2222
logger: &l,
2323
ports: make(map[string]*PortToForward),

‎packages/envd/internal/port/scan.go‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package port
33
import (
44
"time"
55

6+
"github.com/rs/zerolog"
67
"github.com/shirou/gopsutil/v4/net"
78

89
"github.com/e2b-dev/infra/packages/shared/pkg/smap"
910
)
1011

1112
type Scanner struct {
13+
logger *zerolog.Logger
1214
scanExit chan struct{}
1315
subs *smap.Map[*ScannerSubscriber]
1416
period time.Duration
@@ -18,8 +20,9 @@ func (s *Scanner) Destroy() {
1820
close(s.scanExit)
1921
}
2022

21-
func NewScanner(period time.Duration) *Scanner {
23+
func NewScanner(logger *zerolog.Logger, period time.Duration) *Scanner {
2224
return &Scanner{
25+
logger: logger,
2326
period: period,
2427
subs: smap.New[*ScannerSubscriber](),
2528
scanExit: make(chan struct{}),
@@ -40,11 +43,17 @@ func (s *Scanner) ScanAndBroadcast() {
4043

4144
for {
4245
// tcp monitors both ipv4 and ipv6 connections.
43-
processes, _ := net.Connections("tcp")
44-
for _, sub := range s.subs.Items() {
45-
// Pass scanExit so a subscriber that stopped consuming can't park this
46-
// loop on a send and keep it from reaching the select below.
47-
sub.Signal(processes, s.scanExit)
46+
processes, err := net.Connections("tcp")
47+
if err != nil {
48+
if s.logger != nil {
49+
s.logger.Error().Err(err).Msg("Failed to scan open TCP connections")
50+
}
51+
} else {
52+
for _, sub := range s.subs.Items() {
53+
// Pass scanExit so a subscriber that stopped consuming can't park this
54+
// loop on a send and keep it from reaching the select below.
55+
sub.Signal(processes, s.scanExit)
56+
}
4857
}
4958
select {
5059
case <-s.scanExit:
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package port
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
"time"
7+
8+
"github.com/rs/zerolog"
9+
"github.com/shirou/gopsutil/v4/net"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestNewScanner(t *testing.T) {
15+
buf := &bytes.Buffer{}
16+
logger := zerolog.New(buf)
17+
18+
s := NewScanner(&logger, 50*time.Millisecond)
19+
require.NotNil(t, s)
20+
assert.NotNil(t, s.subs)
21+
assert.Equal(t, 50*time.Millisecond, s.period)
22+
23+
sub := s.AddSubscriber("test-sub", &ScannerFilter{
24+
IPs: []string{"127.0.0.1"},
25+
State: "LISTEN",
26+
})
27+
require.NotNil(t, sub)
28+
assert.Equal(t, "test-sub", sub.id)
29+
}
30+
31+
func TestScanAndBroadcastLifecycle(t *testing.T) {
32+
buf := &bytes.Buffer{}
33+
logger := zerolog.New(buf)
34+
35+
s := NewScanner(&logger, 10*time.Millisecond)
36+
sub := s.AddSubscriber("sub-1", nil)
37+
38+
received := make(chan struct{}, 1)
39+
stopDrain := make(chan struct{})
40+
go func() {
41+
for {
42+
select {
43+
case <-stopDrain:
44+
return
45+
case <-sub.Messages:
46+
select {
47+
case received <- struct{}{}:
48+
default:
49+
}
50+
}
51+
}
52+
}()
53+
54+
done := make(chan struct{})
55+
go func() {
56+
s.ScanAndBroadcast()
57+
close(done)
58+
}()
59+
60+
select {
61+
case <-received:
62+
// Received at least one scan message
63+
case <-time.After(1 * time.Second):
64+
t.Fatal("timed out waiting for scan message")
65+
}
66+
67+
s.Destroy()
68+
69+
select {
70+
case <-done:
71+
// Scanner goroutine exited promptly
72+
case <-time.After(1 * time.Second):
73+
t.Fatal("ScanAndBroadcast did not exit after Destroy()")
74+
}
75+
76+
close(stopDrain)
77+
}
78+
79+
func TestSubscriberFilter(t *testing.T) {
80+
sub := NewScannerSubscriber("sub-filter", &ScannerFilter{
81+
IPs: []string{"127.0.0.1", "localhost"},
82+
State: "LISTEN",
83+
})
84+
85+
conns := []net.ConnectionStat{
86+
{
87+
Status: "LISTEN",
88+
Laddr: net.Addr{IP: "127.0.0.1", Port: 8080},
89+
},
90+
{
91+
Status: "ESTABLISHED",
92+
Laddr: net.Addr{IP: "127.0.0.1", Port: 8080},
93+
},
94+
{
95+
Status: "LISTEN",
96+
Laddr: net.Addr{IP: "192.168.1.1", Port: 8080},
97+
},
98+
}
99+
100+
exit := make(chan struct{})
101+
defer close(exit)
102+
103+
go sub.Signal(conns, exit)
104+
105+
select {
106+
case filtered := <-sub.Messages:
107+
require.Len(t, filtered, 1)
108+
assert.Equal(t, uint32(8080), filtered[0].Laddr.Port)
109+
assert.Equal(t, "127.0.0.1", filtered[0].Laddr.IP)
110+
assert.Equal(t, "LISTEN", filtered[0].Status)
111+
case <-time.After(1 * time.Second):
112+
t.Fatal("timed out waiting for filtered messages")
113+
}
114+
}

‎packages/envd/internal/port/scanner_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestScannerSubscriber_Signal_DeliversToSlowReceiver(t *testing.T) {
7373
func TestScanner_Destroy_ExitsWithStalledSubscriber(t *testing.T) {
7474
t.Parallel()
7575

76-
scanner := NewScanner(10 * time.Millisecond)
76+
scanner := NewScanner(nil, 10*time.Millisecond)
7777
scanner.AddSubscriber("stalled-sub", nil)
7878

7979
scanDone := make(chan struct{})

‎packages/envd/main.go‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,12 @@ func run() error {
303303
IdleTimeout: idleTimeout,
304304
}
305305

306+
portLogger := l.With().Str("logger", "port-forwarder").Logger()
307+
306308
// Bind all open ports on 127.0.0.1 and localhost to the eth0 interface
307-
portScanner := publicport.NewScanner(portScannerInterval)
309+
portScanner := publicport.NewScanner(&portLogger, portScannerInterval)
308310
defer portScanner.Destroy()
309311

310-
portLogger := l.With().Str("logger", "port-forwarder").Logger()
311312
portForwarder := publicport.NewForwarder(&portLogger, portScanner, cgroupManager)
312313
if resumeHandover {
313314
// Re-adopt the socats carried across the upgrade before the forwarder's

0 commit comments

Comments
 (0)