Skip to content

Commit ea2027e

Browse files
committed
fixup! feat: add endpoint to get listening ports in agent
1 parent 0f7b8dc commit ea2027e

File tree

4 files changed

+144
-91
lines changed

4 files changed

+144
-91
lines changed

agent/ports_supported.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//go:build linux || windows
2+
// +build linux windows
3+
4+
package agent
5+
6+
import (
7+
"runtime"
8+
"time"
9+
10+
"github.com/cakturk/go-netstat/netstat"
11+
"golang.org/x/xerrors"
12+
13+
"github.com/coder/coder/codersdk"
14+
)
15+
16+
func (lp *listeningPortsHandler) getListeningPorts() ([]codersdk.ListeningPort, error) {
17+
lp.mut.Lock()
18+
defer lp.mut.Unlock()
19+
20+
if runtime.GOOS != "linux" && runtime.GOOS != "windows" {
21+
// Can't scan for ports on non-linux or non-windows systems at the
22+
// moment. The UI will not show any "no ports found" message to the
23+
// user, so the user won't suspect a thing.
24+
return []codersdk.ListeningPort{}, nil
25+
}
26+
27+
if time.Since(lp.mtime) < time.Second {
28+
// copy
29+
ports := make([]codersdk.ListeningPort, len(lp.ports))
30+
copy(ports, lp.ports)
31+
return ports, nil
32+
}
33+
34+
tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
35+
return s.State == netstat.Listen
36+
})
37+
if err != nil {
38+
return nil, xerrors.Errorf("scan listening ports: %w", err)
39+
}
40+
41+
ports := []codersdk.ListeningPort{}
42+
for _, tab := range tabs {
43+
if tab.LocalAddr.Port < uint16(codersdk.MinimumListeningPort) {
44+
continue
45+
}
46+
47+
ports = append(ports, codersdk.ListeningPort{
48+
ProcessName: tab.Process.Name,
49+
Network: codersdk.ListeningPortNetworkTCP,
50+
Port: tab.LocalAddr.Port,
51+
})
52+
}
53+
54+
lp.ports = ports
55+
lp.mtime = time.Now()
56+
57+
// copy
58+
ports = make([]codersdk.ListeningPort, len(lp.ports))
59+
copy(ports, lp.ports)
60+
return ports, nil
61+
}

agent/ports_unsupported.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build !linux && !windows
2+
// +build !linux,!windows
3+
4+
package agent
5+
6+
import "github.com/coder/coder/codersdk"
7+
8+
func (lp *listeningPortsHandler) getListeningPorts() ([]codersdk.ListeningPort, error) {
9+
// Can't scan for ports on non-linux or non-windows systems at the moment.
10+
// The UI will not show any "no ports found" message to the user, so the
11+
// user won't suspect a thing.
12+
return []codersdk.ListeningPort{}, nil
13+
}

agent/statsendpoint.go

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@ package agent
22

33
import (
44
"net/http"
5-
"runtime"
65
"sync"
76
"time"
87

9-
"github.com/cakturk/go-netstat/netstat"
108
"github.com/go-chi/chi"
11-
"golang.org/x/xerrors"
129

1310
"github.com/coder/coder/coderd/httpapi"
1411
"github.com/coder/coder/codersdk"
@@ -34,53 +31,6 @@ type listeningPortsHandler struct {
3431
mtime time.Time
3532
}
3633

37-
func (lp *listeningPortsHandler) getListeningPorts() ([]codersdk.ListeningPort, error) {
38-
lp.mut.Lock()
39-
defer lp.mut.Unlock()
40-
41-
if runtime.GOOS != "linux" && runtime.GOOS != "windows" {
42-
// Can't scan for ports on non-linux or non-windows systems at the
43-
// moment. The UI will not show any "no ports found" message to the
44-
// user, so the user won't suspect a thing.
45-
return []codersdk.ListeningPort{}, nil
46-
}
47-
48-
if time.Since(lp.mtime) < time.Second {
49-
// copy
50-
ports := make([]codersdk.ListeningPort, len(lp.ports))
51-
copy(ports, lp.ports)
52-
return ports, nil
53-
}
54-
55-
tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
56-
return s.State == netstat.Listen
57-
})
58-
if err != nil {
59-
return nil, xerrors.Errorf("scan listening ports: %w", err)
60-
}
61-
62-
ports := []codersdk.ListeningPort{}
63-
for _, tab := range tabs {
64-
if tab.LocalAddr.Port < uint16(codersdk.MinimumListeningPort) {
65-
continue
66-
}
67-
68-
ports = append(ports, codersdk.ListeningPort{
69-
ProcessName: tab.Process.Name,
70-
Network: codersdk.ListeningPortNetworkTCP,
71-
Port: tab.LocalAddr.Port,
72-
})
73-
}
74-
75-
lp.ports = ports
76-
lp.mtime = time.Now()
77-
78-
// copy
79-
ports = make([]codersdk.ListeningPort, len(lp.ports))
80-
copy(ports, lp.ports)
81-
return ports, nil
82-
}
83-
8434
// handler returns a list of listening ports. This is tested by coderd's
8535
// TestWorkspaceAgentListeningPorts test.
8636
func (lp *listeningPortsHandler) handler(rw http.ResponseWriter, r *http.Request) {

coderd/workspaceagents_test.go

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -408,60 +408,89 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) {
408408
CoordinatorDialer: agentClient.ListenWorkspaceAgentTailnet,
409409
Logger: slogtest.Make(t, nil).Named("agent").Leveled(slog.LevelDebug),
410410
})
411-
defer func() {
411+
t.Cleanup(func() {
412412
_ = agentCloser.Close()
413-
}()
413+
})
414414
resources := coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
415415

416-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
417-
defer cancel()
416+
t.Run("LinuxAndWindows", func(t *testing.T) {
417+
t.Parallel()
418+
if runtime.GOOS != "linux" && runtime.GOOS != "windows" {
419+
t.Skip("only runs on linux and windows")
420+
return
421+
}
418422

419-
// Create a TCP listener on a random port that we expect to see in the
420-
// response.
421-
l, err := net.Listen("tcp", "localhost:0")
422-
require.NoError(t, err)
423-
defer l.Close()
424-
tcpAddr, _ := l.Addr().(*net.TCPAddr)
423+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
424+
defer cancel()
425425

426-
// List ports and ensure that the port we expect to see is there.
427-
res, err := client.WorkspaceAgentListeningPorts(ctx, resources[0].Agents[0].ID)
428-
require.NoError(t, err)
426+
// Create a TCP listener on a random port that we expect to see in the
427+
// response.
428+
l, err := net.Listen("tcp", "localhost:0")
429+
require.NoError(t, err)
430+
defer l.Close()
431+
tcpAddr, _ := l.Addr().(*net.TCPAddr)
429432

430-
var (
431-
expected = map[uint16]bool{
432-
// expect the listener we made
433-
uint16(tcpAddr.Port): false,
434-
// expect the coderdtest server
435-
uint16(coderdPort): false,
436-
}
437-
)
438-
for _, port := range res.Ports {
439-
if port.Network == codersdk.ListeningPortNetworkTCP {
440-
if val, ok := expected[port.Port]; ok {
441-
if val {
442-
t.Fatalf("expected to find TCP port %d only once in response", port.Port)
433+
// List ports and ensure that the port we expect to see is there.
434+
res, err := client.WorkspaceAgentListeningPorts(ctx, resources[0].Agents[0].ID)
435+
require.NoError(t, err)
436+
437+
var (
438+
expected = map[uint16]bool{
439+
// expect the listener we made
440+
uint16(tcpAddr.Port): false,
441+
// expect the coderdtest server
442+
uint16(coderdPort): false,
443+
}
444+
)
445+
for _, port := range res.Ports {
446+
if port.Network == codersdk.ListeningPortNetworkTCP {
447+
if val, ok := expected[port.Port]; ok {
448+
if val {
449+
t.Fatalf("expected to find TCP port %d only once in response", port.Port)
450+
}
443451
}
452+
expected[port.Port] = true
444453
}
445-
expected[port.Port] = true
446454
}
447-
}
448-
for port, found := range expected {
449-
if !found {
450-
t.Fatalf("expected to find TCP port %d in response", port)
455+
for port, found := range expected {
456+
if !found {
457+
t.Fatalf("expected to find TCP port %d in response", port)
458+
}
451459
}
452-
}
453460

454-
// Close the listener and check that the port is no longer in the response.
455-
require.NoError(t, l.Close())
456-
time.Sleep(2 * time.Second) // avoid cache
457-
res, err = client.WorkspaceAgentListeningPorts(ctx, resources[0].Agents[0].ID)
458-
require.NoError(t, err)
461+
// Close the listener and check that the port is no longer in the response.
462+
require.NoError(t, l.Close())
463+
time.Sleep(2 * time.Second) // avoid cache
464+
res, err = client.WorkspaceAgentListeningPorts(ctx, resources[0].Agents[0].ID)
465+
require.NoError(t, err)
459466

460-
for _, port := range res.Ports {
461-
if port.Network == codersdk.ListeningPortNetworkTCP && port.Port == uint16(tcpAddr.Port) {
462-
t.Fatalf("expected to not find TCP port %d in response", tcpAddr.Port)
467+
for _, port := range res.Ports {
468+
if port.Network == codersdk.ListeningPortNetworkTCP && port.Port == uint16(tcpAddr.Port) {
469+
t.Fatalf("expected to not find TCP port %d in response", tcpAddr.Port)
470+
}
463471
}
464-
}
472+
})
473+
474+
t.Run("Darwin", func(t *testing.T) {
475+
t.Parallel()
476+
if runtime.GOOS != "darwin" {
477+
t.Skip("only runs on darwin")
478+
return
479+
}
480+
481+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
482+
defer cancel()
483+
484+
// Create a TCP listener on a random port.
485+
l, err := net.Listen("tcp", "localhost:0")
486+
require.NoError(t, err)
487+
defer l.Close()
488+
489+
// List ports and ensure that the list is empty because we're on darwin.
490+
res, err := client.WorkspaceAgentListeningPorts(ctx, resources[0].Agents[0].ID)
491+
require.NoError(t, err)
492+
require.Len(t, res.Ports, 0)
493+
})
465494
}
466495

467496
func TestWorkspaceAgentAppHealth(t *testing.T) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy