Skip to content

Commit 0833658

Browse files
committed
Merge remote-tracking branch 'origin/main' into 168-gcp-aws-marketplace-update
2 parents 4e39583 + 700a453 commit 0833658

File tree

93 files changed

+3642
-670
lines changed

Some content is hidden

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

93 files changed

+3642
-670
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Generated files
2+
agent/agentcontainers/acmock/acmock.go linguist-generated=true
23
coderd/apidoc/docs.go linguist-generated=true
34
docs/reference/api/*.md linguist-generated=true
45
docs/reference/cli/*.md linguist-generated=true

.github/workflows/ci.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -940,11 +940,7 @@ jobs:
940940
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
941941
runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }}
942942
steps:
943-
- name: Harden Runner
944-
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
945-
with:
946-
egress-policy: audit
947-
943+
# Harden Runner doesn't work on macOS
948944
- name: Checkout
949945
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
950946
with:
@@ -957,6 +953,11 @@ jobs:
957953
echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH
958954
echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
959955
956+
- name: Switch XCode Version
957+
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
958+
with:
959+
xcode-version: "16.0.0"
960+
960961
- name: Setup Go
961962
uses: ./.github/actions/setup-go
962963

.github/workflows/dogfood.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
5454

5555
- name: Set up Docker Buildx
56-
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
56+
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0
5757

5858
- name: Login to DockerHub
5959
if: github.ref == 'refs/heads/main'

.github/workflows/release.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ jobs:
3636
build-dylib:
3737
runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }}
3838
steps:
39-
- name: Harden Runner
40-
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
41-
with:
42-
egress-policy: audit
43-
39+
# Harden Runner doesn't work on macOS.
4440
- name: Checkout
4541
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
4642
with:
@@ -61,6 +57,11 @@ jobs:
6157
echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH
6258
echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
6359
60+
- name: Switch XCode Version
61+
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
62+
with:
63+
xcode-version: "16.0.0"
64+
6465
- name: Setup Go
6566
uses: ./.github/actions/setup-go
6667

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ jobs:
4747

4848
# Upload the results to GitHub's code scanning dashboard.
4949
- name: "Upload to code-scanning"
50-
uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
50+
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
5151
with:
5252
sarif_file: results.sarif

.github/workflows/security.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
uses: ./.github/actions/setup-go
3939

4040
- name: Initialize CodeQL
41-
uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
41+
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
4242
with:
4343
languages: go, javascript
4444

@@ -48,7 +48,7 @@ jobs:
4848
rm Makefile
4949
5050
- name: Perform CodeQL Analysis
51-
uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
51+
uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
5252

5353
- name: Send Slack notification on failure
5454
if: ${{ failure() }}
@@ -144,7 +144,7 @@ jobs:
144144
severity: "CRITICAL,HIGH"
145145

146146
- name: Upload Trivy scan results to GitHub Security tab
147-
uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
147+
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
148148
with:
149149
sarif_file: trivy-results.sarif
150150
category: "Trivy"

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,8 @@ GEN_FILES := \
563563
site/e2e/provisionerGenerated.ts \
564564
examples/examples.gen.json \
565565
$(TAILNETTEST_MOCKS) \
566-
coderd/database/pubsub/psmock/psmock.go
566+
coderd/database/pubsub/psmock/psmock.go \
567+
agent/agentcontainers/acmock/acmock.go
567568

568569

569570
# all gen targets should be added here and to gen/mark-fresh
@@ -598,6 +599,7 @@ gen/mark-fresh:
598599
examples/examples.gen.json \
599600
$(TAILNETTEST_MOCKS) \
600601
coderd/database/pubsub/psmock/psmock.go \
602+
agent/agentcontainers/acmock/acmock.go \
601603
"
602604

603605
for file in $$files; do
@@ -629,6 +631,9 @@ coderd/database/dbmock/dbmock.go: coderd/database/db.go coderd/database/querier.
629631
coderd/database/pubsub/psmock/psmock.go: coderd/database/pubsub/pubsub.go
630632
go generate ./coderd/database/pubsub/psmock
631633

634+
agent/agentcontainers/acmock/acmock.go: agent/agentcontainers/containers.go
635+
go generate ./agent/agentcontainers/acmock/
636+
632637
$(TAILNETTEST_MOCKS): tailnet/coordinator.go tailnet/service.go
633638
go generate ./tailnet/tailnettest/
634639

agent/agent.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"tailscale.com/util/clientmetric"
3434

3535
"cdr.dev/slog"
36+
"github.com/coder/coder/v2/agent/agentcontainers"
3637
"github.com/coder/coder/v2/agent/agentexec"
3738
"github.com/coder/coder/v2/agent/agentscripts"
3839
"github.com/coder/coder/v2/agent/agentssh"
@@ -82,6 +83,7 @@ type Options struct {
8283
ServiceBannerRefreshInterval time.Duration
8384
BlockFileTransfer bool
8485
Execer agentexec.Execer
86+
ContainerLister agentcontainers.Lister
8587
}
8688

8789
type Client interface {
@@ -122,7 +124,7 @@ func New(options Options) Agent {
122124
options.ScriptDataDir = options.TempDir
123125
}
124126
if options.ExchangeToken == nil {
125-
options.ExchangeToken = func(ctx context.Context) (string, error) {
127+
options.ExchangeToken = func(_ context.Context) (string, error) {
126128
return "", nil
127129
}
128130
}
@@ -144,6 +146,9 @@ func New(options Options) Agent {
144146
if options.Execer == nil {
145147
options.Execer = agentexec.DefaultExecer
146148
}
149+
if options.ContainerLister == nil {
150+
options.ContainerLister = agentcontainers.NewDocker(options.Execer)
151+
}
147152

148153
hardCtx, hardCancel := context.WithCancel(context.Background())
149154
gracefulCtx, gracefulCancel := context.WithCancel(hardCtx)
@@ -178,6 +183,7 @@ func New(options Options) Agent {
178183
prometheusRegistry: prometheusRegistry,
179184
metrics: newAgentMetrics(prometheusRegistry),
180185
execer: options.Execer,
186+
lister: options.ContainerLister,
181187
}
182188
// Initially, we have a closed channel, reflecting the fact that we are not initially connected.
183189
// Each time we connect we replace the channel (while holding the closeMutex) with a new one
@@ -247,6 +253,7 @@ type agent struct {
247253
// labeled in Coder with the agent + workspace.
248254
metrics *agentMetrics
249255
execer agentexec.Execer
256+
lister agentcontainers.Lister
250257
}
251258

252259
func (a *agent) TailnetConn() *tailnet.Conn {

agent/agentcontainers/acmock/acmock.go

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

agent/agentcontainers/acmock/doc.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Package acmock contains a mock implementation of agentcontainers.Lister for use in tests.
2+
package acmock
3+
4+
//go:generate mockgen -destination ./acmock.go -package acmock .. Lister

agent/agentcontainers/containers.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package agentcontainers
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
"slices"
8+
"time"
9+
10+
"golang.org/x/xerrors"
11+
12+
"github.com/coder/coder/v2/coderd/httpapi"
13+
"github.com/coder/coder/v2/codersdk"
14+
"github.com/coder/quartz"
15+
)
16+
17+
const (
18+
defaultGetContainersCacheDuration = 10 * time.Second
19+
dockerCreatedAtTimeFormat = "2006-01-02 15:04:05 -0700 MST"
20+
getContainersTimeout = 5 * time.Second
21+
)
22+
23+
type devcontainersHandler struct {
24+
cacheDuration time.Duration
25+
cl Lister
26+
clock quartz.Clock
27+
28+
// lockCh protects the below fields. We use a channel instead of a mutex so we
29+
// can handle cancellation properly.
30+
lockCh chan struct{}
31+
containers *codersdk.WorkspaceAgentListContainersResponse
32+
mtime time.Time
33+
}
34+
35+
// Option is a functional option for devcontainersHandler.
36+
type Option func(*devcontainersHandler)
37+
38+
// WithLister sets the agentcontainers.Lister implementation to use.
39+
// The default implementation uses the Docker CLI to list containers.
40+
func WithLister(cl Lister) Option {
41+
return func(ch *devcontainersHandler) {
42+
ch.cl = cl
43+
}
44+
}
45+
46+
// New returns a new devcontainersHandler with the given options applied.
47+
func New(options ...Option) http.Handler {
48+
ch := &devcontainersHandler{
49+
lockCh: make(chan struct{}, 1),
50+
}
51+
for _, opt := range options {
52+
opt(ch)
53+
}
54+
return ch
55+
}
56+
57+
func (ch *devcontainersHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
58+
select {
59+
case <-r.Context().Done():
60+
// Client went away.
61+
return
62+
default:
63+
ct, err := ch.getContainers(r.Context())
64+
if err != nil {
65+
if errors.Is(err, context.Canceled) {
66+
httpapi.Write(r.Context(), rw, http.StatusRequestTimeout, codersdk.Response{
67+
Message: "Could not get containers.",
68+
Detail: "Took too long to list containers.",
69+
})
70+
return
71+
}
72+
httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
73+
Message: "Could not get containers.",
74+
Detail: err.Error(),
75+
})
76+
return
77+
}
78+
79+
httpapi.Write(r.Context(), rw, http.StatusOK, ct)
80+
}
81+
}
82+
83+
func (ch *devcontainersHandler) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) {
84+
select {
85+
case <-ctx.Done():
86+
return codersdk.WorkspaceAgentListContainersResponse{}, ctx.Err()
87+
default:
88+
ch.lockCh <- struct{}{}
89+
}
90+
defer func() {
91+
<-ch.lockCh
92+
}()
93+
94+
// make zero-value usable
95+
if ch.cacheDuration == 0 {
96+
ch.cacheDuration = defaultGetContainersCacheDuration
97+
}
98+
if ch.cl == nil {
99+
ch.cl = &DockerCLILister{}
100+
}
101+
if ch.containers == nil {
102+
ch.containers = &codersdk.WorkspaceAgentListContainersResponse{}
103+
}
104+
if ch.clock == nil {
105+
ch.clock = quartz.NewReal()
106+
}
107+
108+
now := ch.clock.Now()
109+
if now.Sub(ch.mtime) < ch.cacheDuration {
110+
// Return a copy of the cached data to avoid accidental modification by the caller.
111+
cpy := codersdk.WorkspaceAgentListContainersResponse{
112+
Containers: slices.Clone(ch.containers.Containers),
113+
Warnings: slices.Clone(ch.containers.Warnings),
114+
}
115+
return cpy, nil
116+
}
117+
118+
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, getContainersTimeout)
119+
defer timeoutCancel()
120+
updated, err := ch.cl.List(timeoutCtx)
121+
if err != nil {
122+
return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("get containers: %w", err)
123+
}
124+
ch.containers = &updated
125+
ch.mtime = now
126+
127+
// Return a copy of the cached data to avoid accidental modification by the
128+
// caller.
129+
cpy := codersdk.WorkspaceAgentListContainersResponse{
130+
Containers: slices.Clone(ch.containers.Containers),
131+
Warnings: slices.Clone(ch.containers.Warnings),
132+
}
133+
return cpy, nil
134+
}
135+
136+
// Lister is an interface for listing containers visible to the
137+
// workspace agent.
138+
type Lister interface {
139+
// List returns a list of containers visible to the workspace agent.
140+
// This should include running and stopped containers.
141+
List(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error)
142+
}

0 commit comments

Comments
 (0)