Skip to content

Commit b17a602

Browse files
authored
Merge branch 'main' into fix-aws-windows
2 parents 1f39a4c + c547591 commit b17a602

File tree

303 files changed

+17933
-10833
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

303 files changed

+17933
-10833
lines changed

.github/pull_request_template.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ jobs:
512512
- name: Install node_modules
513513
run: ./scripts/yarn_install.sh
514514

515-
- run: yarn test:ci
515+
- run: yarn test:ci --max-workers ${{ steps.cpu-cores.outputs.count }}
516516
working-directory: site
517517

518518
- uses: codecov/codecov-action@v3

.github/workflows/contrib.yaml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,6 @@ jobs:
2727
steps:
2828
- uses: hmarr/auto-approve-action@v3
2929
if: github.actor == 'dependabot[bot]'
30-
auto-approve-docs:
31-
runs-on: ubuntu-latest
32-
if: github.event_name == 'pull_request_target'
33-
permissions:
34-
pull-requests: write
35-
steps:
36-
- uses: actions/checkout@v3
37-
- name: Get changed files in the docs folder
38-
id: changed-files
39-
uses: tj-actions/changed-files@v35
40-
with:
41-
files: docs/*
42-
- uses: hmarr/auto-approve-action@v3
43-
if: github.actor == 'bpmct' && steps.changed-files.outputs.only_changed == 'true'
4430

4531
cla:
4632
runs-on: ubuntu-latest

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ site/test-results/*
2727
site/e2e/test-results/*
2828
site/e2e/states/*.json
2929
site/playwright-report/*
30+
site/.swc
3031

3132
# Make target for updating golden files.
3233
cli/testdata/.gen-golden
34+
helm/tests/testdata/.gen-golden
3335

3436
# Build
3537
/build/

.golangci.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ linters:
215215
- asciicheck
216216
- bidichk
217217
- bodyclose
218-
- deadcode
219218
- dogsled
220219
- errcheck
221220
- errname
@@ -259,4 +258,3 @@ linters:
259258
- typecheck
260259
- unconvert
261260
- unused
262-
- varcheck

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ site/test-results/*
3030
site/e2e/test-results/*
3131
site/e2e/states/*.json
3232
site/playwright-report/*
33+
site/.swc
3334

3435
# Make target for updating golden files.
3536
cli/testdata/.gen-golden
37+
helm/tests/testdata/.gen-golden
3638

3739
# Build
3840
/build/

Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me
501501
yarn run format:write:only ../docs/admin/prometheus.md
502502

503503
docs/cli.md: scripts/clidocgen/main.go $(GO_SRC_FILES) docs/manifest.json
504-
rm -rf ./docs/cli/*.md
504+
# TODO(@ammario): re-enable server.md once we finish clibase migration.
505+
ls ./docs/cli/*.md | grep -vP "\/coder_server" | xargs rm
505506
BASE_PATH="." go run ./scripts/clidocgen
506507
cd site
507508
yarn run format:write:only ../docs/cli.md ../docs/cli/*.md ../docs/manifest.json
@@ -515,13 +516,17 @@ coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS)
515516
./scripts/apidocgen/generate.sh
516517
yarn run --cwd=site format:write:only ../docs/api ../docs/manifest.json ../coderd/apidoc/swagger.json
517518

518-
update-golden-files: cli/testdata/.gen-golden
519+
update-golden-files: cli/testdata/.gen-golden helm/tests/testdata/.gen-golden
519520
.PHONY: update-golden-files
520521

521522
cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(GO_SRC_FILES)
522523
go test ./cli -run=TestCommandHelp -update
523524
touch "$@"
524525

526+
helm/tests/testdata/.gen-golden: $(wildcard helm/tests/testdata/*.golden) $(GO_SRC_FILES)
527+
go test ./helm/tests -run=TestUpdateGoldenFiles -update
528+
touch "$@"
529+
525530
# Generate a prettierrc for the site package that uses relative paths for
526531
# overrides. This allows us to share the same prettier config between the
527532
# site and the root of the repo.

agent/agent.go

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"os/exec"
1818
"os/user"
1919
"path/filepath"
20+
"reflect"
2021
"runtime"
2122
"sort"
2223
"strconv"
@@ -60,7 +61,7 @@ const (
6061

6162
// MagicSSHSessionTypeEnvironmentVariable is used to track the purpose behind an SSH connection.
6263
// This is stripped from any commands being executed, and is counted towards connection stats.
63-
MagicSSHSessionTypeEnvironmentVariable = "__CODER_SSH_SESSION_TYPE"
64+
MagicSSHSessionTypeEnvironmentVariable = "CODER_SSH_SESSION_TYPE"
6465
// MagicSSHSessionTypeVSCode is set in the SSH config by the VS Code extension to identify itself.
6566
MagicSSHSessionTypeVSCode = "vscode"
6667
// MagicSSHSessionTypeJetBrains is set in the SSH config by the JetBrains extension to identify itself.
@@ -76,6 +77,7 @@ type Options struct {
7677
ReconnectingPTYTimeout time.Duration
7778
EnvironmentVariables map[string]string
7879
Logger slog.Logger
80+
AgentPorts map[int]string
7981
}
8082

8183
type Client interface {
@@ -122,9 +124,8 @@ func New(options Options) io.Closer {
122124
tempDir: options.TempDir,
123125
lifecycleUpdate: make(chan struct{}, 1),
124126
lifecycleReported: make(chan codersdk.WorkspaceAgentLifecycle, 1),
125-
// TODO: This is a temporary hack to make tests not flake.
126-
// @kylecarbs has a better solution in here: https://github.com/coder/coder/pull/6469
127-
connStatsChan: make(chan *agentsdk.Stats, 8),
127+
ignorePorts: options.AgentPorts,
128+
connStatsChan: make(chan *agentsdk.Stats, 1),
128129
}
129130
a.init(ctx)
130131
return a
@@ -137,6 +138,10 @@ type agent struct {
137138
filesystem afero.Fs
138139
logDir string
139140
tempDir string
141+
// ignorePorts tells the api handler which ports to ignore when
142+
// listing all listening ports. This is helpful to hide ports that
143+
// are used by the agent, that the user does not care about.
144+
ignorePorts map[int]string
140145

141146
reconnectingPTYs sync.Map
142147
reconnectingPTYTimeout time.Duration
@@ -159,11 +164,8 @@ type agent struct {
159164

160165
network *tailnet.Conn
161166
connStatsChan chan *agentsdk.Stats
167+
latestStat atomic.Pointer[agentsdk.Stats]
162168

163-
statRxPackets atomic.Int64
164-
statRxBytes atomic.Int64
165-
statTxPackets atomic.Int64
166-
statTxBytes atomic.Int64
167169
connCountVSCode atomic.Int64
168170
connCountJetBrains atomic.Int64
169171
connCountReconnectingPTY atomic.Int64
@@ -905,10 +907,13 @@ func (a *agent) handleSSHSession(session ssh.Session) (retErr error) {
905907
switch magicType {
906908
case MagicSSHSessionTypeVSCode:
907909
a.connCountVSCode.Add(1)
910+
defer a.connCountVSCode.Add(-1)
908911
case MagicSSHSessionTypeJetBrains:
909912
a.connCountJetBrains.Add(1)
913+
defer a.connCountJetBrains.Add(-1)
910914
case "":
911915
a.connCountSSHSession.Add(1)
916+
defer a.connCountSSHSession.Add(-1)
912917
default:
913918
a.logger.Warn(ctx, "invalid magic ssh session type specified", slog.F("type", magicType))
914919
}
@@ -1012,6 +1017,7 @@ func (a *agent) handleReconnectingPTY(ctx context.Context, logger slog.Logger, m
10121017
defer conn.Close()
10131018

10141019
a.connCountReconnectingPTY.Add(1)
1020+
defer a.connCountReconnectingPTY.Add(-1)
10151021

10161022
connectionID := uuid.NewString()
10171023
logger = logger.With(slog.F("id", msg.ID), slog.F("connection_id", connectionID))
@@ -1210,18 +1216,15 @@ func (a *agent) startReportingConnectionStats(ctx context.Context) {
12101216
ConnectionCount: int64(len(networkStats)),
12111217
ConnectionsByProto: map[string]int64{},
12121218
}
1213-
// Tailscale resets counts on every report!
1214-
// We'd rather have these compound, like Linux does!
12151219
for conn, counts := range networkStats {
12161220
stats.ConnectionsByProto[conn.Proto.String()]++
1217-
stats.RxBytes = a.statRxBytes.Add(int64(counts.RxBytes))
1218-
stats.RxPackets = a.statRxPackets.Add(int64(counts.RxPackets))
1219-
stats.TxBytes = a.statTxBytes.Add(int64(counts.TxBytes))
1220-
stats.TxPackets = a.statTxPackets.Add(int64(counts.TxPackets))
1221+
stats.RxBytes += int64(counts.RxBytes)
1222+
stats.RxPackets += int64(counts.RxPackets)
1223+
stats.TxBytes += int64(counts.TxBytes)
1224+
stats.TxPackets += int64(counts.TxPackets)
12211225
}
12221226

1223-
// Tailscale's connection stats are not cumulative, but it makes no sense to make
1224-
// ours temporary.
1227+
// The count of active sessions.
12251228
stats.SessionCountSSH = a.connCountSSHSession.Load()
12261229
stats.SessionCountVSCode = a.connCountVSCode.Load()
12271230
stats.SessionCountJetBrains = a.connCountJetBrains.Load()
@@ -1270,10 +1273,16 @@ func (a *agent) startReportingConnectionStats(ctx context.Context) {
12701273
// Convert from microseconds to milliseconds.
12711274
stats.ConnectionMedianLatencyMS /= 1000
12721275

1276+
lastStat := a.latestStat.Load()
1277+
if lastStat != nil && reflect.DeepEqual(lastStat, stats) {
1278+
a.logger.Info(ctx, "skipping stat because nothing changed")
1279+
return
1280+
}
1281+
a.latestStat.Store(stats)
1282+
12731283
select {
12741284
case a.connStatsChan <- stats:
1275-
default:
1276-
a.logger.Warn(ctx, "network stat dropped")
1285+
case <-a.closed:
12771286
}
12781287
}
12791288

agent/agent_test.go

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ func TestAgent_Stats_SSH(t *testing.T) {
6868
session, err := sshClient.NewSession()
6969
require.NoError(t, err)
7070
defer session.Close()
71-
require.NoError(t, session.Run("echo test"))
71+
stdin, err := session.StdinPipe()
72+
require.NoError(t, err)
73+
err = session.Shell()
74+
require.NoError(t, err)
7275

7376
var s *agentsdk.Stats
7477
require.Eventuallyf(t, func() bool {
@@ -78,6 +81,9 @@ func TestAgent_Stats_SSH(t *testing.T) {
7881
}, testutil.WaitLong, testutil.IntervalFast,
7982
"never saw stats: %+v", s,
8083
)
84+
_ = stdin.Close()
85+
err = session.Wait()
86+
require.NoError(t, err)
8187
}
8288

8389
func TestAgent_Stats_ReconnectingPTY(t *testing.T) {
@@ -112,43 +118,69 @@ func TestAgent_Stats_ReconnectingPTY(t *testing.T) {
112118

113119
func TestAgent_Stats_Magic(t *testing.T) {
114120
t.Parallel()
121+
t.Run("StripsEnvironmentVariable", func(t *testing.T) {
122+
t.Parallel()
123+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
124+
defer cancel()
125+
//nolint:dogsled
126+
conn, _, _, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
127+
sshClient, err := conn.SSHClient(ctx)
128+
require.NoError(t, err)
129+
defer sshClient.Close()
130+
session, err := sshClient.NewSession()
131+
require.NoError(t, err)
132+
session.Setenv(agent.MagicSSHSessionTypeEnvironmentVariable, agent.MagicSSHSessionTypeVSCode)
133+
defer session.Close()
115134

116-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
117-
defer cancel()
118-
119-
//nolint:dogsled
120-
conn, _, stats, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
121-
sshClient, err := conn.SSHClient(ctx)
122-
require.NoError(t, err)
123-
defer sshClient.Close()
124-
session, err := sshClient.NewSession()
125-
require.NoError(t, err)
126-
session.Setenv(agent.MagicSSHSessionTypeEnvironmentVariable, agent.MagicSSHSessionTypeVSCode)
127-
defer session.Close()
128-
129-
command := "sh -c 'echo $" + agent.MagicSSHSessionTypeEnvironmentVariable + "'"
130-
expected := ""
131-
if runtime.GOOS == "windows" {
132-
expected = "%" + agent.MagicSSHSessionTypeEnvironmentVariable + "%"
133-
command = "cmd.exe /c echo " + expected
134-
}
135-
output, err := session.Output(command)
136-
require.NoError(t, err)
137-
require.Equal(t, expected, strings.TrimSpace(string(output)))
138-
var s *agentsdk.Stats
139-
require.Eventuallyf(t, func() bool {
140-
var ok bool
141-
s, ok = <-stats
142-
return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0 &&
143-
// Ensure that the connection didn't count as a "normal" SSH session.
144-
// This was a special one, so it should be labeled specially in the stats!
145-
s.SessionCountVSCode == 1 &&
146-
// Ensure that connection latency is being counted!
147-
// If it isn't, it's set to -1.
148-
s.ConnectionMedianLatencyMS >= 0
149-
}, testutil.WaitLong, testutil.IntervalFast,
150-
"never saw stats: %+v", s,
151-
)
135+
command := "sh -c 'echo $" + agent.MagicSSHSessionTypeEnvironmentVariable + "'"
136+
expected := ""
137+
if runtime.GOOS == "windows" {
138+
expected = "%" + agent.MagicSSHSessionTypeEnvironmentVariable + "%"
139+
command = "cmd.exe /c echo " + expected
140+
}
141+
output, err := session.Output(command)
142+
require.NoError(t, err)
143+
require.Equal(t, expected, strings.TrimSpace(string(output)))
144+
})
145+
t.Run("Tracks", func(t *testing.T) {
146+
t.Parallel()
147+
if runtime.GOOS == "window" {
148+
t.Skip("Sleeping for infinity doesn't work on Windows")
149+
}
150+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
151+
defer cancel()
152+
//nolint:dogsled
153+
conn, _, stats, _, _ := setupAgent(t, agentsdk.Metadata{}, 0)
154+
sshClient, err := conn.SSHClient(ctx)
155+
require.NoError(t, err)
156+
defer sshClient.Close()
157+
session, err := sshClient.NewSession()
158+
require.NoError(t, err)
159+
session.Setenv(agent.MagicSSHSessionTypeEnvironmentVariable, agent.MagicSSHSessionTypeVSCode)
160+
defer session.Close()
161+
stdin, err := session.StdinPipe()
162+
require.NoError(t, err)
163+
err = session.Shell()
164+
require.NoError(t, err)
165+
var s *agentsdk.Stats
166+
require.Eventuallyf(t, func() bool {
167+
var ok bool
168+
s, ok = <-stats
169+
return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0 &&
170+
// Ensure that the connection didn't count as a "normal" SSH session.
171+
// This was a special one, so it should be labeled specially in the stats!
172+
s.SessionCountVSCode == 1 &&
173+
// Ensure that connection latency is being counted!
174+
// If it isn't, it's set to -1.
175+
s.ConnectionMedianLatencyMS >= 0
176+
}, testutil.WaitLong, testutil.IntervalFast,
177+
"never saw stats: %+v", s,
178+
)
179+
// The shell will automatically exit if there is no stdin!
180+
_ = stdin.Close()
181+
err = session.Wait()
182+
require.NoError(t, err)
183+
})
152184
}
153185

154186
func TestAgent_SessionExec(t *testing.T) {

agent/api.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,32 @@ import (
1111
"github.com/coder/coder/codersdk"
1212
)
1313

14-
func (*agent) apiHandler() http.Handler {
14+
func (a *agent) apiHandler() http.Handler {
1515
r := chi.NewRouter()
1616
r.Get("/", func(rw http.ResponseWriter, r *http.Request) {
1717
httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.Response{
1818
Message: "Hello from the agent!",
1919
})
2020
})
2121

22-
lp := &listeningPortsHandler{}
22+
// Make a copy to ensure the map is not modified after the handler is
23+
// created.
24+
cpy := make(map[int]string)
25+
for k, b := range a.ignorePorts {
26+
cpy[k] = b
27+
}
28+
29+
lp := &listeningPortsHandler{ignorePorts: cpy}
2330
r.Get("/api/v0/listening-ports", lp.handler)
2431

2532
return r
2633
}
2734

2835
type listeningPortsHandler struct {
29-
mut sync.Mutex
30-
ports []codersdk.WorkspaceAgentListeningPort
31-
mtime time.Time
36+
mut sync.Mutex
37+
ports []codersdk.WorkspaceAgentListeningPort
38+
mtime time.Time
39+
ignorePorts map[int]string
3240
}
3341

3442
// handler returns a list of listening ports. This is tested by coderd's

0 commit comments

Comments
 (0)