Skip to content

Commit 63572a5

Browse files
committed
Merge branch 'main' into parameter
2 parents 1e05960 + e740aeb commit 63572a5

File tree

290 files changed

+9287
-3313
lines changed

Some content is hidden

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

290 files changed

+9287
-3313
lines changed

.github/workflows/coder.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Checkout
3535
uses: actions/checkout@v2
3636
- name: typos-action
37-
uses: crate-ci/typos@v1.12.8
37+
uses: crate-ci/typos@v1.12.12
3838
with:
3939
config: .github/workflows/typos.toml
4040
- name: Fix Helper
@@ -89,14 +89,14 @@ jobs:
8989
style-lint-golangci:
9090
name: style/lint/golangci
9191
timeout-minutes: 5
92-
runs-on: ubuntu-latest
92+
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
9393
steps:
9494
- uses: actions/checkout@v3
9595
- uses: actions/setup-go@v3
9696
with:
9797
go-version: "~1.19"
9898
- name: golangci-lint
99-
uses: golangci/golangci-lint-action@v3.2.0
99+
uses: golangci/golangci-lint-action@v3.3.0
100100
with:
101101
version: v1.48.0
102102

@@ -171,7 +171,7 @@ jobs:
171171
gen:
172172
name: "style/gen"
173173
timeout-minutes: 8
174-
runs-on: ubuntu-latest
174+
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
175175
needs: changes
176176
if: needs.changes.outputs.docs-only == 'false'
177177
steps:
@@ -276,7 +276,7 @@ jobs:
276276
277277
test-go:
278278
name: "test/go"
279-
runs-on: ${{ matrix.os }}
279+
runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || matrix.os }}
280280
timeout-minutes: 20
281281
strategy:
282282
matrix:
@@ -356,7 +356,7 @@ jobs:
356356

357357
test-go-postgres:
358358
name: "test/go/postgres"
359-
runs-on: ubuntu-latest
359+
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
360360
# This timeout must be greater than the timeout set by `go test` in
361361
# `make test-postgres` to ensure we receive a trace of running
362362
# goroutines. Setting this to the timeout +5m should work quite well
@@ -417,7 +417,7 @@ jobs:
417417

418418
deploy:
419419
name: "deploy"
420-
runs-on: ubuntu-latest
420+
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
421421
timeout-minutes: 30
422422
needs: changes
423423
if: |

.github/workflows/dogfood.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
steps:
1818
- name: Get branch name
1919
id: branch-name
20-
uses: tj-actions/branch-names@v6.1
20+
uses: tj-actions/branch-names@v6.2
2121

2222
- name: "Branch name to Docker tag name"
2323
id: docker-tag-name

.github/workflows/release.yaml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ permissions:
2020
contents: write
2121
# Necessary to push docker images to ghcr.io.
2222
packages: write
23+
# Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage)
24+
id-token: write
2325

2426
env:
2527
CODER_RELEASE: ${{ github.event.inputs.snapshot && 'false' || 'true' }}
2628

2729
jobs:
2830
release:
29-
runs-on: ubuntu-latest
31+
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
3032
env:
3133
# Necessary for Docker manifest
3234
DOCKER_CLI_EXPERIMENTAL: "enabled"
@@ -167,6 +169,26 @@ jobs:
167169
env:
168170
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
169171

172+
- name: Authenticate to Google Cloud
173+
uses: google-github-actions/auth@v0
174+
with:
175+
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_ID_PROVIDER }}
176+
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
177+
178+
- name: Setup GCloud SDK
179+
uses: 'google-github-actions/setup-gcloud@v0'
180+
181+
- name: Publish Helm Chart
182+
run: |
183+
set -euo pipefail
184+
version="$(./scripts/version.sh)"
185+
mkdir -p build/helm
186+
cp "build/coder_helm_${version}.tgz" build/helm
187+
gsutil cp gs://helm.coder.com/v2/index.yaml build/helm/index.yaml
188+
helm repo index build/helm --url https://helm.coder.com/v2 --merge build/helm/index.yaml
189+
gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/coder_helm_${version}.tgz gs://helm.coder.com/v2
190+
gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/index.yaml gs://helm.coder.com/v2
191+
170192
- name: Upload artifacts to actions (if dry-run or snapshot)
171193
if: ${{ github.event.inputs.dry_run || github.event.inputs.snapshot }}
172194
uses: actions/upload-artifact@v2

.github/workflows/typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ MacOS = "macOS"
77
[default.extend-words]
88
# do as sudo replacement
99
doas = "doas"
10+
darcula = "darcula"
1011

1112
[files]
1213
extend-exclude = [

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ gotests.coverage
1818
.gitpod.yml
1919
.DS_Store
2020

21+
# Make target for updating golden files.
22+
cli/testdata/.gen-golden
23+
2124
# Front-end ignore
2225
.next/
2326
site/.eslintcache

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,15 @@ site/src/api/typesGenerated.ts: scripts/apitypings/main.go $(shell find codersdk
443443
cd site
444444
yarn run format:types
445445

446+
update-golden-files: cli/testdata/.gen-golden
447+
.PHONY: update-golden-files
448+
449+
cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) \
450+
$(shell find . -not -path './vendor/*' -type f -name '*.go')
451+
452+
go test ./cli -run=TestCommandHelp -update
453+
touch "$@"
454+
446455
test: test-clean
447456
gotestsum -- -v -short ./...
448457
.PHONY: test

agent/agent.go

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const (
5656

5757
type Options struct {
5858
Filesystem afero.Fs
59-
ExchangeToken func(ctx context.Context) error
59+
ExchangeToken func(ctx context.Context) (string, error)
6060
Client Client
6161
ReconnectingPTYTimeout time.Duration
6262
EnvironmentVariables map[string]string
@@ -78,6 +78,11 @@ func New(options Options) io.Closer {
7878
if options.Filesystem == nil {
7979
options.Filesystem = afero.NewOsFs()
8080
}
81+
if options.ExchangeToken == nil {
82+
options.ExchangeToken = func(ctx context.Context) (string, error) {
83+
return "", nil
84+
}
85+
}
8186
ctx, cancelFunc := context.WithCancel(context.Background())
8287
server := &agent{
8388
reconnectingPTYTimeout: options.ReconnectingPTYTimeout,
@@ -97,7 +102,7 @@ func New(options Options) io.Closer {
97102
type agent struct {
98103
logger slog.Logger
99104
client Client
100-
exchangeToken func(ctx context.Context) error
105+
exchangeToken func(ctx context.Context) (string, error)
101106
filesystem afero.Fs
102107

103108
reconnectingPTYs sync.Map
@@ -110,8 +115,9 @@ type agent struct {
110115

111116
envVars map[string]string
112117
// metadata is atomic because values can change after reconnection.
113-
metadata atomic.Value
114-
sshServer *ssh.Server
118+
metadata atomic.Value
119+
sessionToken atomic.Pointer[string]
120+
sshServer *ssh.Server
115121

116122
network *tailnet.Conn
117123
stats *Stats
@@ -147,14 +153,13 @@ func (a *agent) run(ctx context.Context) error {
147153
// This allows the agent to refresh it's token if necessary.
148154
// For instance identity this is required, since the instance
149155
// may not have re-provisioned, but a new agent ID was created.
150-
if a.exchangeToken != nil {
151-
err := a.exchangeToken(ctx)
152-
if err != nil {
153-
return xerrors.Errorf("exchange token: %w", err)
154-
}
156+
sessionToken, err := a.exchangeToken(ctx)
157+
if err != nil {
158+
return xerrors.Errorf("exchange token: %w", err)
155159
}
160+
a.sessionToken.Store(&sessionToken)
156161

157-
err := a.client.PostWorkspaceAgentVersion(ctx, buildinfo.Version())
162+
err = a.client.PostWorkspaceAgentVersion(ctx, buildinfo.Version())
158163
if err != nil {
159164
return xerrors.Errorf("update workspace agent version: %w", err)
160165
}
@@ -221,12 +226,18 @@ func (a *agent) run(ctx context.Context) error {
221226
}
222227

223228
func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*tailnet.Conn, error) {
229+
a.closeMutex.Lock()
230+
if a.isClosed() {
231+
a.closeMutex.Unlock()
232+
return nil, xerrors.New("closed")
233+
}
224234
network, err := tailnet.NewConn(&tailnet.Options{
225235
Addresses: []netip.Prefix{netip.PrefixFrom(codersdk.TailnetIP, 128)},
226236
DERPMap: derpMap,
227237
Logger: a.logger.Named("tailnet"),
228238
})
229239
if err != nil {
240+
a.closeMutex.Unlock()
230241
return nil, xerrors.Errorf("create tailnet: %w", err)
231242
}
232243
a.network = network
@@ -237,12 +248,15 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
237248
}
238249
return a.stats.wrapConn(conn)
239250
})
251+
a.connCloseWait.Add(4)
252+
a.closeMutex.Unlock()
240253

241254
sshListener, err := network.Listen("tcp", ":"+strconv.Itoa(codersdk.TailnetSSHPort))
242255
if err != nil {
243256
return nil, xerrors.Errorf("listen on the ssh port: %w", err)
244257
}
245258
go func() {
259+
defer a.connCloseWait.Done()
246260
for {
247261
conn, err := sshListener.Accept()
248262
if err != nil {
@@ -257,6 +271,7 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
257271
return nil, xerrors.Errorf("listen for reconnecting pty: %w", err)
258272
}
259273
go func() {
274+
defer a.connCloseWait.Done()
260275
for {
261276
conn, err := reconnectingPTYListener.Accept()
262277
if err != nil {
@@ -291,6 +306,7 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
291306
return nil, xerrors.Errorf("listen for speedtest: %w", err)
292307
}
293308
go func() {
309+
defer a.connCloseWait.Done()
294310
for {
295311
conn, err := speedtestListener.Accept()
296312
if err != nil {
@@ -312,6 +328,7 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
312328
return nil, xerrors.Errorf("listen for statistics: %w", err)
313329
}
314330
go func() {
331+
defer a.connCloseWait.Done()
315332
defer statisticsListener.Close()
316333
server := &http.Server{
317334
Handler: a.statisticsHandler(),
@@ -553,13 +570,15 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
553570
// Set environment variables reliable detection of being inside a
554571
// Coder workspace.
555572
cmd.Env = append(cmd.Env, "CODER=true")
556-
557573
cmd.Env = append(cmd.Env, fmt.Sprintf("USER=%s", username))
558574
// Git on Windows resolves with UNIX-style paths.
559575
// If using backslashes, it's unable to find the executable.
560576
unixExecutablePath := strings.ReplaceAll(executablePath, "\\", "/")
561577
cmd.Env = append(cmd.Env, fmt.Sprintf(`GIT_SSH_COMMAND=%s gitssh --`, unixExecutablePath))
562578

579+
// Specific Coder subcommands require the agent token exposed!
580+
cmd.Env = append(cmd.Env, fmt.Sprintf("CODER_AGENT_TOKEN=%s", *a.sessionToken.Load()))
581+
563582
// Set SSH connection environment variables (these are also set by OpenSSH
564583
// and thus expected to be present by SSH clients). Since the agent does
565584
// networking in-memory, trying to provide accurate values here would be
@@ -569,6 +588,13 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
569588
cmd.Env = append(cmd.Env, fmt.Sprintf("SSH_CLIENT=%s %s %s", srcAddr, srcPort, dstPort))
570589
cmd.Env = append(cmd.Env, fmt.Sprintf("SSH_CONNECTION=%s %s %s %s", srcAddr, srcPort, dstAddr, dstPort))
571590

591+
// This adds the ports dialog to code-server that enables
592+
// proxying a port dynamically.
593+
cmd.Env = append(cmd.Env, fmt.Sprintf("VSCODE_PROXY_URI=%s", metadata.VSCodePortProxyURI))
594+
595+
// Hide Coder message on code-server's "Getting Started" page
596+
cmd.Env = append(cmd.Env, "CS_DISABLE_GETTING_STARTED_OVERRIDE=true")
597+
572598
// Load environment variables passed via the agent.
573599
// These should override all variables we manually specify.
574600
for envKey, value := range metadata.EnvironmentVariables {

agent/agent_test.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"os/exec"
1313
"os/user"
14+
"path"
1415
"path/filepath"
1516
"runtime"
1617
"strconv"
@@ -22,6 +23,7 @@ import (
2223

2324
"golang.org/x/xerrors"
2425
"tailscale.com/net/speedtest"
26+
"tailscale.com/tailcfg"
2527

2628
scp "github.com/bramvdbogaerde/go-scp"
2729
"github.com/google/uuid"
@@ -231,7 +233,13 @@ func TestAgent(t *testing.T) {
231233
require.NoError(t, err, "get working directory")
232234
require.Equal(t, home, wd, "working directory should be home user home")
233235
tempFile := filepath.Join(t.TempDir(), "sftp")
234-
file, err := client.Create(tempFile)
236+
// SFTP only accepts unix-y paths.
237+
remoteFile := filepath.ToSlash(tempFile)
238+
if !path.IsAbs(remoteFile) {
239+
// On Windows, e.g. "/C:/Users/...".
240+
remoteFile = path.Join("/", remoteFile)
241+
}
242+
file, err := client.Create(remoteFile)
235243
require.NoError(t, err)
236244
err = file.Close()
237245
require.NoError(t, err)
@@ -525,9 +533,9 @@ func TestAgent(t *testing.T) {
525533
}
526534
initialized := atomic.Int32{}
527535
closer := agent.New(agent.Options{
528-
ExchangeToken: func(ctx context.Context) error {
536+
ExchangeToken: func(ctx context.Context) (string, error) {
529537
initialized.Add(1)
530-
return nil
538+
return "", nil
531539
},
532540
Client: client,
533541
Logger: slogtest.Make(t, nil).Leveled(slog.LevelInfo),
@@ -552,14 +560,15 @@ func TestAgent(t *testing.T) {
552560
agentID: uuid.New(),
553561
metadata: codersdk.WorkspaceAgentMetadata{
554562
GitAuthConfigs: 1,
563+
DERPMap: &tailcfg.DERPMap{},
555564
},
556565
statsChan: make(chan *codersdk.AgentStats),
557566
coordinator: tailnet.NewCoordinator(),
558567
}
559568
filesystem := afero.NewMemMapFs()
560569
closer := agent.New(agent.Options{
561-
ExchangeToken: func(ctx context.Context) error {
562-
return nil
570+
ExchangeToken: func(ctx context.Context) (string, error) {
571+
return "", nil
563572
},
564573
Client: client,
565574
Logger: slogtest.Make(t, nil).Leveled(slog.LevelInfo),

agent/apphealth.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func NewWorkspaceAppHealthReporter(logger slog.Logger, apps []codersdk.Workspace
3333
hasHealthchecksEnabled := false
3434
health := make(map[string]codersdk.WorkspaceAppHealth, 0)
3535
for _, app := range apps {
36-
health[app.Name] = app.Health
36+
health[app.DisplayName] = app.Health
3737
if !hasHealthchecksEnabled && app.Health != codersdk.WorkspaceAppHealthDisabled {
3838
hasHealthchecksEnabled = true
3939
}
@@ -85,21 +85,21 @@ func NewWorkspaceAppHealthReporter(logger slog.Logger, apps []codersdk.Workspace
8585
}()
8686
if err != nil {
8787
mu.Lock()
88-
if failures[app.Name] < int(app.Healthcheck.Threshold) {
88+
if failures[app.DisplayName] < int(app.Healthcheck.Threshold) {
8989
// increment the failure count and keep status the same.
9090
// we will change it when we hit the threshold.
91-
failures[app.Name]++
91+
failures[app.DisplayName]++
9292
} else {
9393
// set to unhealthy if we hit the failure threshold.
9494
// we stop incrementing at the threshold to prevent the failure value from increasing forever.
95-
health[app.Name] = codersdk.WorkspaceAppHealthUnhealthy
95+
health[app.DisplayName] = codersdk.WorkspaceAppHealthUnhealthy
9696
}
9797
mu.Unlock()
9898
} else {
9999
mu.Lock()
100100
// we only need one successful health check to be considered healthy.
101-
health[app.Name] = codersdk.WorkspaceAppHealthHealthy
102-
failures[app.Name] = 0
101+
health[app.DisplayName] = codersdk.WorkspaceAppHealthHealthy
102+
failures[app.DisplayName] = 0
103103
mu.Unlock()
104104
}
105105

0 commit comments

Comments
 (0)