Skip to content

Commit 0ba8fa2

Browse files
committed
Merge branch 'main' into colin/rm-wsconncache2
2 parents b5ea538 + 1406838 commit 0ba8fa2

File tree

101 files changed

+3538
-513
lines changed

Some content is hidden

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

101 files changed

+3538
-513
lines changed

.github/workflows/ci.yaml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ jobs:
4747
docs:
4848
- "docs/**"
4949
- "README.md"
50-
# For testing:
51-
# - ".github/**"
50+
- "examples/templates/**"
51+
- "examples/web-server/**"
52+
- "examples/monitoring/**"
53+
- "examples/lima/**"
5254
go:
5355
- "**.sql"
5456
- "**.go"
@@ -231,7 +233,7 @@ jobs:
231233

232234
- uses: hashicorp/setup-terraform@v2
233235
with:
234-
terraform_version: 1.1.9
236+
terraform_version: 1.5.1
235237
terraform_wrapper: false
236238

237239
- name: Test with Mock Database
@@ -296,7 +298,7 @@ jobs:
296298

297299
- uses: hashicorp/setup-terraform@v2
298300
with:
299-
terraform_version: 1.1.9
301+
terraform_version: 1.5.1
300302
terraform_wrapper: false
301303

302304
- name: Test with PostgreSQL Database
@@ -338,6 +340,11 @@ jobs:
338340

339341
- uses: ./.github/actions/setup-go
340342

343+
- uses: hashicorp/setup-terraform@v2
344+
with:
345+
terraform_version: 1.5.1
346+
terraform_wrapper: false
347+
341348
- name: Run Tests
342349
run: |
343350
gotestsum --junitfile="gotests.xml" -- -race ./...
@@ -474,7 +481,7 @@ jobs:
474481

475482
- uses: hashicorp/setup-terraform@v2
476483
with:
477-
terraform_version: 1.1.9
484+
terraform_version: 1.5.1
478485
terraform_wrapper: false
479486

480487
- name: Build

.github/workflows/pr-cleanup.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
fi
2626
2727
- name: Delete image
28+
continue-on-error: true
2829
uses: bots-house/ghcr-delete-image-action@v1.1.0
2930
with:
3031
owner: coder
@@ -33,20 +34,17 @@ jobs:
3334
tag: pr${{ steps.pr_number.outputs.PR_NUMBER }}
3435

3536
- name: Set up kubeconfig
36-
if: always()
3737
run: |
3838
set -euxo pipefail
3939
mkdir -p ~/.kube
4040
echo "${{ secrets.DELIVERYBOT_KUBECONFIG }}" > ~/.kube/config
4141
export KUBECONFIG=~/.kube/config
4242
4343
- name: Delete helm release
44-
if: always()
4544
run: |
4645
set -euxo pipefail
4746
helm delete --namespace "pr${{ steps.pr_number.outputs.PR_NUMBER }}" "pr${{ steps.pr_number.outputs.PR_NUMBER }}" || echo "helm release not found"
4847
4948
- name: "Remove PR namespace"
50-
if: always()
5149
run: |
5250
kubectl delete namespace "pr${{ steps.pr_number.outputs.PR_NUMBER }}" || echo "namespace not found"

agent/agent.go

Lines changed: 19 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package agent
22

33
import (
4-
"bufio"
54
"bytes"
65
"context"
76
"encoding/binary"
@@ -876,26 +875,30 @@ func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err er
876875
}
877876
cmd := cmdPty.AsExec()
878877

879-
var writer io.Writer = fileWriter
878+
var stdout, stderr io.Writer = fileWriter, fileWriter
880879
if lifecycle == "startup" {
881-
// Create pipes for startup logs reader and writer
882-
logsReader, logsWriter := io.Pipe()
880+
send, flushAndClose := agentsdk.StartupLogsSender(a.client.PatchStartupLogs, logger)
881+
// If ctx is canceled here (or in a writer below), we may be
882+
// discarding logs, but that's okay because we're shutting down
883+
// anyway. We could consider creating a new context here if we
884+
// want better control over flush during shutdown.
883885
defer func() {
884-
_ = logsReader.Close()
885-
}()
886-
writer = io.MultiWriter(fileWriter, logsWriter)
887-
flushedLogs, err := a.trackScriptLogs(ctx, logsReader)
888-
if err != nil {
889-
return xerrors.Errorf("track %s script logs: %w", lifecycle, err)
890-
}
891-
defer func() {
892-
_ = logsWriter.Close()
893-
<-flushedLogs
886+
if err := flushAndClose(ctx); err != nil {
887+
logger.Warn(ctx, "flush startup logs failed", slog.Error(err))
888+
}
894889
}()
890+
891+
infoW := agentsdk.StartupLogsWriter(ctx, send, codersdk.LogLevelInfo)
892+
defer infoW.Close()
893+
errW := agentsdk.StartupLogsWriter(ctx, send, codersdk.LogLevelError)
894+
defer errW.Close()
895+
896+
stdout = io.MultiWriter(fileWriter, infoW)
897+
stderr = io.MultiWriter(fileWriter, errW)
895898
}
896899

897-
cmd.Stdout = writer
898-
cmd.Stderr = writer
900+
cmd.Stdout = stdout
901+
cmd.Stderr = stderr
899902

900903
start := time.Now()
901904
defer func() {
@@ -926,143 +929,6 @@ func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err er
926929
return nil
927930
}
928931

929-
func (a *agent) trackScriptLogs(ctx context.Context, reader io.ReadCloser) (chan struct{}, error) {
930-
// Synchronous sender, there can only be one outbound send at a time.
931-
//
932-
// It's important that we either flush or drop all logs before returning
933-
// because the startup state is reported after flush.
934-
sendDone := make(chan struct{})
935-
send := make(chan []agentsdk.StartupLog, 1)
936-
go func() {
937-
// Set flushTimeout and backlogLimit so that logs are uploaded
938-
// once every 250ms or when 100 logs have been added to the
939-
// backlog, whichever comes first.
940-
flushTimeout := 250 * time.Millisecond
941-
backlogLimit := 100
942-
943-
flush := time.NewTicker(flushTimeout)
944-
945-
var backlog []agentsdk.StartupLog
946-
defer func() {
947-
flush.Stop()
948-
_ = reader.Close() // Ensure read routine is closed.
949-
if len(backlog) > 0 {
950-
a.logger.Debug(ctx, "track script logs sender exiting, discarding logs", slog.F("discarded_logs_count", len(backlog)))
951-
}
952-
a.logger.Debug(ctx, "track script logs sender exited")
953-
close(sendDone)
954-
}()
955-
956-
done := false
957-
for {
958-
flushed := false
959-
select {
960-
case <-ctx.Done():
961-
return
962-
case <-a.closed:
963-
return
964-
// Close (!ok) can be triggered by the reader closing due to
965-
// EOF or due to agent closing, when this happens we attempt
966-
// a final flush. If the context is canceled this will be a
967-
// no-op.
968-
case logs, ok := <-send:
969-
done = !ok
970-
if ok {
971-
backlog = append(backlog, logs...)
972-
flushed = len(backlog) >= backlogLimit
973-
}
974-
case <-flush.C:
975-
flushed = true
976-
}
977-
978-
if (done || flushed) && len(backlog) > 0 {
979-
flush.Stop() // Lower the chance of a double flush.
980-
981-
// Retry uploading logs until successful or a specific
982-
// error occurs.
983-
for r := retry.New(time.Second, 5*time.Second); r.Wait(ctx); {
984-
err := a.client.PatchStartupLogs(ctx, agentsdk.PatchStartupLogs{
985-
Logs: backlog,
986-
})
987-
if err == nil {
988-
break
989-
}
990-
991-
if errors.Is(err, context.Canceled) {
992-
return
993-
}
994-
var sdkErr *codersdk.Error
995-
if errors.As(err, &sdkErr) {
996-
if sdkErr.StatusCode() == http.StatusRequestEntityTooLarge {
997-
a.logger.Warn(ctx, "startup logs too large, dropping logs")
998-
break
999-
}
1000-
}
1001-
a.logger.Error(ctx, "upload startup logs failed", slog.Error(err), slog.F("to_send", backlog))
1002-
}
1003-
if ctx.Err() != nil {
1004-
return
1005-
}
1006-
backlog = nil
1007-
1008-
// Anchor flush to the last log upload.
1009-
flush.Reset(flushTimeout)
1010-
}
1011-
if done {
1012-
return
1013-
}
1014-
}
1015-
}()
1016-
1017-
// Forward read lines to the sender or queue them for when the
1018-
// sender is ready to process them.
1019-
//
1020-
// We only need to track this goroutine since it will ensure that
1021-
// the sender has closed before returning.
1022-
logsDone := make(chan struct{})
1023-
err := a.trackConnGoroutine(func() {
1024-
defer func() {
1025-
close(send)
1026-
<-sendDone
1027-
a.logger.Debug(ctx, "track script logs reader exited")
1028-
close(logsDone)
1029-
}()
1030-
1031-
var queue []agentsdk.StartupLog
1032-
1033-
s := bufio.NewScanner(reader)
1034-
for s.Scan() {
1035-
select {
1036-
case <-ctx.Done():
1037-
return
1038-
case <-a.closed:
1039-
return
1040-
case queue = <-send:
1041-
// Not captured by sender yet, re-use.
1042-
default:
1043-
}
1044-
1045-
queue = append(queue, agentsdk.StartupLog{
1046-
CreatedAt: database.Now(),
1047-
Output: s.Text(),
1048-
})
1049-
send <- queue
1050-
queue = nil
1051-
}
1052-
if err := s.Err(); err != nil {
1053-
a.logger.Warn(ctx, "scan startup logs ended unexpectedly", slog.Error(err))
1054-
}
1055-
})
1056-
if err != nil {
1057-
close(send)
1058-
<-sendDone
1059-
close(logsDone)
1060-
return logsDone, err
1061-
}
1062-
1063-
return logsDone, nil
1064-
}
1065-
1066932
func (a *agent) handleReconnectingPTY(ctx context.Context, logger slog.Logger, msg codersdk.WorkspaceAgentReconnectingPTYInit, conn net.Conn) (retErr error) {
1067933
defer conn.Close()
1068934
a.metrics.connectionsTotal.Add(1)

cli/clistat/cgroup.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ func (s *Statter) ContainerCPU() (*Result, error) {
8484
}
8585

8686
r := &Result{
87-
Unit: "cores",
88-
Used: used2 - used1,
89-
Total: ptr.To(total),
87+
Unit: "cores",
88+
Used: used2 - used1,
89+
Total: ptr.To(total),
90+
Prefix: PrefixDefault,
9091
}
9192
return r, nil
9293
}
@@ -184,20 +185,20 @@ func (s *Statter) cGroupV1CPUUsed() (float64, error) {
184185

185186
// ContainerMemory returns the memory usage of the container cgroup.
186187
// If the system is not containerized, this always returns nil.
187-
func (s *Statter) ContainerMemory() (*Result, error) {
188+
func (s *Statter) ContainerMemory(p Prefix) (*Result, error) {
188189
if ok, err := IsContainerized(s.fs); err != nil || !ok {
189190
return nil, nil //nolint:nilnil
190191
}
191192

192193
if s.isCGroupV2() {
193-
return s.cGroupV2Memory()
194+
return s.cGroupV2Memory(p)
194195
}
195196

196197
// Fall back to CGroupv1
197-
return s.cGroupV1Memory()
198+
return s.cGroupV1Memory(p)
198199
}
199200

200-
func (s *Statter) cGroupV2Memory() (*Result, error) {
201+
func (s *Statter) cGroupV2Memory(p Prefix) (*Result, error) {
201202
maxUsageBytes, err := readInt64(s.fs, cgroupV2MemoryMaxBytes)
202203
if err != nil {
203204
return nil, xerrors.Errorf("read memory total: %w", err)
@@ -214,13 +215,14 @@ func (s *Statter) cGroupV2Memory() (*Result, error) {
214215
}
215216

216217
return &Result{
217-
Total: ptr.To(float64(maxUsageBytes)),
218-
Used: float64(currUsageBytes - inactiveFileBytes),
219-
Unit: "B",
218+
Total: ptr.To(float64(maxUsageBytes)),
219+
Used: float64(currUsageBytes - inactiveFileBytes),
220+
Unit: "B",
221+
Prefix: p,
220222
}, nil
221223
}
222224

223-
func (s *Statter) cGroupV1Memory() (*Result, error) {
225+
func (s *Statter) cGroupV1Memory(p Prefix) (*Result, error) {
224226
maxUsageBytes, err := readInt64(s.fs, cgroupV1MemoryMaxUsageBytes)
225227
if err != nil {
226228
return nil, xerrors.Errorf("read memory total: %w", err)
@@ -239,9 +241,10 @@ func (s *Statter) cGroupV1Memory() (*Result, error) {
239241

240242
// Total memory used is usage - total_inactive_file
241243
return &Result{
242-
Total: ptr.To(float64(maxUsageBytes)),
243-
Used: float64(usageBytes - totalInactiveFileBytes),
244-
Unit: "B",
244+
Total: ptr.To(float64(maxUsageBytes)),
245+
Used: float64(usageBytes - totalInactiveFileBytes),
246+
Unit: "B",
247+
Prefix: p,
245248
}, nil
246249
}
247250

cli/clistat/disk.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
// Disk returns the disk usage of the given path.
1212
// If path is empty, it returns the usage of the root directory.
13-
func (*Statter) Disk(path string) (*Result, error) {
13+
func (*Statter) Disk(p Prefix, path string) (*Result, error) {
1414
if path == "" {
1515
path = "/"
1616
}
@@ -22,5 +22,6 @@ func (*Statter) Disk(path string) (*Result, error) {
2222
r.Total = ptr.To(float64(stat.Blocks * uint64(stat.Bsize)))
2323
r.Used = float64(stat.Blocks-stat.Bfree) * float64(stat.Bsize)
2424
r.Unit = "B"
25+
r.Prefix = p
2526
return &r, nil
2627
}

cli/clistat/disk_windows.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
// Disk returns the disk usage of the given path.
99
// If path is empty, it defaults to C:\
10-
func (*Statter) Disk(path string) (*Result, error) {
10+
func (*Statter) Disk(p Prefix, path string) (*Result, error) {
1111
if path == "" {
1212
path = `C:\`
1313
}
@@ -31,5 +31,6 @@ func (*Statter) Disk(path string) (*Result, error) {
3131
r.Total = ptr.To(float64(totalBytes))
3232
r.Used = float64(totalBytes - freeBytes)
3333
r.Unit = "B"
34+
r.Prefix = p
3435
return &r, nil
3536
}

0 commit comments

Comments
 (0)