Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore(vpn): send ping results over tunnel#18200

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ethanndickson merged 13 commits intomainfromethan/send-ping-results
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes fromall 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
45 changes: 8 additions & 37 deletionscli/ssh.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,6 @@ import (
"path/filepath"
"regexp"
"slices"
"strconv"
"strings"
"sync"
"time"
Expand All@@ -31,7 +30,6 @@ import (
"golang.org/x/term"
"golang.org/x/xerrors"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"tailscale.com/tailcfg"
"tailscale.com/types/netlogtype"

"cdr.dev/slog"
Expand All@@ -40,11 +38,13 @@ import (
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/cli/cliutil"
"github.com/coder/coder/v2/coderd/autobuild/notify"
"github.com/coder/coder/v2/coderd/util/maps"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/pty"
"github.com/coder/coder/v2/tailnet"
"github.com/coder/quartz"
"github.com/coder/retry"
"github.com/coder/serpent"
Expand DownExpand Up@@ -1456,28 +1456,6 @@ func collectNetworkStats(ctx context.Context, agentConn *workspacesdk.AgentConn,
}
node := agentConn.Node()
derpMap := agentConn.DERPMap()
derpLatency := map[string]float64{}

// Convert DERP region IDs to friendly names for display in the UI.
for rawRegion, latency := range node.DERPLatency {
regionParts := strings.SplitN(rawRegion, "-", 2)
regionID, err := strconv.Atoi(regionParts[0])
if err != nil {
continue
}
region, found := derpMap.Regions[regionID]
if !found {
// It's possible that a workspace agent is using an old DERPMap
// and reports regions that do not exist. If that's the case,
// report the region as unknown!
region = &tailcfg.DERPRegion{
RegionID: regionID,
RegionName: fmt.Sprintf("Unnamed %d", regionID),
}
}
// Convert the microseconds to milliseconds.
derpLatency[region.RegionName] = latency * 1000
}

totalRx := uint64(0)
totalTx := uint64(0)
Expand All@@ -1491,27 +1469,20 @@ func collectNetworkStats(ctx context.Context, agentConn *workspacesdk.AgentConn,
uploadSecs := float64(totalTx) / dur.Seconds()
downloadSecs := float64(totalRx) / dur.Seconds()

// Sometimes the preferred DERP doesn't match the one we're actually
// connected with. Perhaps because the agent prefers a different DERP and
// we're using that server instead.
preferredDerpID := node.PreferredDERP
if pingResult.DERPRegionID != 0 {
preferredDerpID = pingResult.DERPRegionID
}
preferredDerp, ok := derpMap.Regions[preferredDerpID]
preferredDerpName := fmt.Sprintf("Unnamed %d", preferredDerpID)
if ok {
preferredDerpName = preferredDerp.RegionName
}
preferredDerpName := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
derpLatency := tailnet.ExtractDERPLatency(node, derpMap)
if _, ok := derpLatency[preferredDerpName]; !ok {
derpLatency[preferredDerpName] = 0
}
derpLatencyMs := maps.Map(derpLatency, func(dur time.Duration) float64 {
return float64(dur) / float64(time.Millisecond)
})

return &sshNetworkStats{
P2P: p2p,
Latency: float64(latency.Microseconds()) / 1000,
PreferredDERP: preferredDerpName,
DERPLatency:derpLatency,
DERPLatency:derpLatencyMs,
UploadBytesSec: int64(uploadSecs),
DownloadBytesSec: int64(downloadSecs),
}, nil
Expand Down
8 changes: 0 additions & 8 deletionscoderd/database/db2sdk/db2sdk.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,14 +47,6 @@ func ListLazy[F any, T any](convert func(F) T) func(list []F) []T {
}
}

func Map[K comparable, F any, T any](params map[K]F, convert func(F) T) map[K]T {
into := make(map[K]T)
for k, item := range params {
into[k] = convert(item)
}
return into
}

type ExternalAuthMeta struct {
Authenticated bool
ValidateError string
Expand Down
8 changes: 8 additions & 0 deletionscoderd/util/maps/maps.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,14 @@ import (
"golang.org/x/exp/constraints"
)

func Map[K comparable, F any, T any](params map[K]F, convert func(F) T) map[K]T {
into := make(map[K]T)
for k, item := range params {
into[k] = convert(item)
}
return into
}

// Subset returns true if all the keys of a are present
// in b and have the same values.
// If the corresponding value of a[k] is the zero value in
Expand Down
46 changes: 46 additions & 0 deletionstailnet/derpmap.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,8 +8,11 @@ import (
"net/http"
"os"
"strconv"
"strings"
"time"

"golang.org/x/xerrors"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
)

Expand DownExpand Up@@ -152,6 +155,49 @@ regionLoop:
return derpMap, nil
}

func ExtractPreferredDERPName(pingResult *ipnstate.PingResult, node *Node, derpMap *tailcfg.DERPMap) string {
// Sometimes the preferred DERP doesn't match the one we're actually
// connected with. Perhaps because the agent prefers a different DERP and
// we're using that server instead.
preferredDerpID := node.PreferredDERP
if pingResult.DERPRegionID != 0 {
preferredDerpID = pingResult.DERPRegionID
}
preferredDerp, ok := derpMap.Regions[preferredDerpID]
preferredDerpName := fmt.Sprintf("Unnamed %d", preferredDerpID)
if ok {
preferredDerpName = preferredDerp.RegionName
}

return preferredDerpName
}

// ExtractDERPLatency extracts a map of derp region names to their latencies
func ExtractDERPLatency(node *Node, derpMap *tailcfg.DERPMap) map[string]time.Duration {
latencyMs := make(map[string]time.Duration)

// Convert DERP region IDs to friendly names for display in the UI.
for rawRegion, latency := range node.DERPLatency {
regionParts := strings.SplitN(rawRegion, "-", 2)
regionID, err := strconv.Atoi(regionParts[0])
if err != nil {
continue
}
region, found := derpMap.Regions[regionID]
if !found {
// It's possible that a workspace agent is using an old DERPMap
// and reports regions that do not exist. If that's the case,
// report the region as unknown!
region = &tailcfg.DERPRegion{
RegionID: regionID,
RegionName: fmt.Sprintf("Unnamed %d", regionID),
}
}
latencyMs[region.RegionName] = time.Duration(latency * float64(time.Second))
}
return latencyMs
}

// CompareDERPMaps returns true if the given DERPMaps are equivalent. Ordering
// of slices is ignored.
//
Expand Down
109 changes: 109 additions & 0 deletionstailnet/derpmap_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"

"github.com/coder/coder/v2/tailnet"
Expand DownExpand Up@@ -162,3 +163,111 @@ func TestNewDERPMap(t *testing.T) {
require.ErrorContains(t, err, "DERP map has no DERP nodes")
})
}

func TestExtractDERPLatency(t *testing.T) {
t.Parallel()

derpMap := &tailcfg.DERPMap{
Regions: map[int]*tailcfg.DERPRegion{
1: {
RegionID: 1,
RegionName: "Region One",
Nodes: []*tailcfg.DERPNode{
{Name: "node1", RegionID: 1},
},
},
2: {
RegionID: 2,
RegionName: "Region Two",
Nodes: []*tailcfg.DERPNode{
{Name: "node2", RegionID: 2},
},
},
},
}

t.Run("Basic", func(t *testing.T) {
t.Parallel()
node := &tailnet.Node{
DERPLatency: map[string]float64{
"1-node1": 0.05,
"2-node2": 0.1,
},
}
latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
require.EqualValues(t, 50, latencyMs["Region One"].Milliseconds())
require.EqualValues(t, 100, latencyMs["Region Two"].Milliseconds())
require.Len(t, latencyMs, 2)
})

t.Run("UnknownRegion", func(t *testing.T) {
t.Parallel()
node := &tailnet.Node{
DERPLatency: map[string]float64{
"999-node999": 0.2,
},
}
latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
require.EqualValues(t, 200, latencyMs["Unnamed 999"].Milliseconds())
require.Len(t, latencyMs, 1)
})

t.Run("InvalidRegionFormat", func(t *testing.T) {
t.Parallel()
node := &tailnet.Node{
DERPLatency: map[string]float64{
"invalid": 0.3,
"1-node1": 0.05,
"abc-node": 0.15,
},
}
latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
require.EqualValues(t, 50, latencyMs["Region One"].Milliseconds())
require.Len(t, latencyMs, 1)
require.NotContains(t, latencyMs, "invalid")
require.NotContains(t, latencyMs, "abc-node")
})

t.Run("EmptyInput", func(t *testing.T) {
t.Parallel()
node := &tailnet.Node{
DERPLatency: map[string]float64{},
}
latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
require.Empty(t, latencyMs)
})
}

func TestExtractPreferredDERPName(t *testing.T) {
t.Parallel()
derpMap := &tailcfg.DERPMap{
Regions: map[int]*tailcfg.DERPRegion{
1: {RegionName: "New York"},
2: {RegionName: "London"},
},
}

t.Run("UsesPingRegion", func(t *testing.T) {
t.Parallel()
pingResult := &ipnstate.PingResult{DERPRegionID: 2}
node := &tailnet.Node{PreferredDERP: 1}
result := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
require.Equal(t, "London", result)
})

t.Run("UsesNodePreferred", func(t *testing.T) {
t.Parallel()
pingResult := &ipnstate.PingResult{DERPRegionID: 0}
node := &tailnet.Node{PreferredDERP: 1}
result := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
require.Equal(t, "New York", result)
})

t.Run("UnknownRegion", func(t *testing.T) {
t.Parallel()
pingResult := &ipnstate.PingResult{DERPRegionID: 99}
node := &tailnet.Node{PreferredDERP: 1}
result := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
require.Equal(t, "Unnamed 99", result)
})
}
10 changes: 10 additions & 0 deletionsvpn/client.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,11 +5,14 @@ import (
"net/http"
"net/netip"
"net/url"
"time"

"golang.org/x/xerrors"

"tailscale.com/ipn/ipnstate"
"tailscale.com/net/dns"
"tailscale.com/net/netmon"
"tailscale.com/tailcfg"
"tailscale.com/wgengine/router"

"github.com/google/uuid"
Expand All@@ -27,6 +30,9 @@ import (
type Conn interface {
CurrentWorkspaceState() (tailnet.WorkspaceUpdate, error)
GetPeerDiagnostics(peerID uuid.UUID) tailnet.PeerDiagnostics
Ping(ctx context.Context, agentID uuid.UUID) (time.Duration, bool, *ipnstate.PingResult, error)
Node() *tailnet.Node
DERPMap() *tailcfg.DERPMap
Close() error
}

Expand All@@ -38,6 +44,10 @@ type vpnConn struct {
updatesCtrl *tailnet.TunnelAllWorkspaceUpdatesController
}

func (c *vpnConn) Ping(ctx context.Context, agentID uuid.UUID) (time.Duration, bool, *ipnstate.PingResult, error) {
return c.Conn.Ping(ctx, tailnet.TailscaleServicePrefix.AddrFromUUID(agentID))
}

func (c *vpnConn) CurrentWorkspaceState() (tailnet.WorkspaceUpdate, error) {
return c.updatesCtrl.CurrentState()
}
Expand Down
9 changes: 2 additions & 7 deletionsvpn/speaker_internal_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,8 @@ func TestMain(m *testing.M) {
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
}

const expectedHandshake = "codervpn tunnel 1.2\n"

// TestSpeaker_RawPeer tests the speaker with a peer that we simulate by directly making reads and
// writes to the other end of the pipe. There should be at least one test that does this, rather
// than use 2 speakers so that we don't have a bug where we don't adhere to the stated protocol, but
Expand All@@ -48,8 +50,6 @@ func TestSpeaker_RawPeer(t *testing.T) {
errCh <- err
}()

expectedHandshake := "codervpn tunnel 1.1\n"

b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
Expand DownExpand Up@@ -157,8 +157,6 @@ func TestSpeaker_OversizeHandshake(t *testing.T) {
errCh <- err
}()

expectedHandshake := "codervpn tunnel 1.1\n"

b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
Expand DownExpand Up@@ -210,7 +208,6 @@ func TestSpeaker_HandshakeInvalid(t *testing.T) {
_, err = mp.Write([]byte(tc.handshake))
require.NoError(t, err)

expectedHandshake := "codervpn tunnel 1.1\n"
b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
Expand DownExpand Up@@ -248,8 +245,6 @@ func TestSpeaker_CorruptMessage(t *testing.T) {
errCh <- err
}()

expectedHandshake := "codervpn tunnel 1.1\n"

b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp