Skip to content

Commit 1280b6a

Browse files
committed
Merge remote-tracking branch 'origin/main' into jjs/internal-527
2 parents 05c28d1 + 00fb610 commit 1280b6a

File tree

260 files changed

+11886
-4095
lines changed

Some content is hidden

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

260 files changed

+11886
-4095
lines changed

.github/ISSUE_TEMPLATE/1-bug.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: "🐞 Bug"
22
description: "File a bug report."
33
title: "bug: "
44
labels: ["needs-triage"]
5+
type: "Bug"
56
body:
67
- type: checkboxes
78
id: existing_issues

.github/workflows/contrib.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
# branch should not be protected
4343
branch: "main"
4444
# Some users have signed a corporate CLA with Coder so are exempt from signing our community one.
45-
allowlist: "coryb,aaronlehmann,dependabot*"
45+
allowlist: "coryb,aaronlehmann,dependabot*,blink-so*"
4646

4747
release-labels:
4848
runs-on: ubuntu-latest

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,10 @@ We are always working on new integrations. Please feel free to open an issue and
109109
### Official
110110

111111
- [**VS Code Extension**](https://marketplace.visualstudio.com/items?itemName=coder.coder-remote): Open any Coder workspace in VS Code with a single click
112-
- [**JetBrains Gateway Extension**](https://plugins.jetbrains.com/plugin/19620-coder): Open any Coder workspace in JetBrains Gateway with a single click
112+
- [**JetBrains Toolbox Plugin**](https://plugins.jetbrains.com/plugin/26968-coder): Open any Coder workspace from JetBrains Toolbox with a single click
113+
- [**JetBrains Gateway Plugin**](https://plugins.jetbrains.com/plugin/19620-coder): Open any Coder workspace in JetBrains Gateway with a single click
113114
- [**Dev Container Builder**](https://github.com/coder/envbuilder): Build development environments using `devcontainer.json` on Docker, Kubernetes, and OpenShift
114-
- [**Module Registry**](https://registry.coder.com): Extend development environments with common use-cases
115+
- [**Coder Registry**](https://registry.coder.com): Build and extend development environments with common use-cases
115116
- [**Kubernetes Log Stream**](https://github.com/coder/coder-logstream-kube): Stream Kubernetes Pod events to the Coder startup logs
116117
- [**Self-Hosted VS Code Extension Marketplace**](https://github.com/coder/code-marketplace): A private extension marketplace that works in restricted or airgapped networks integrating with [code-server](https://github.com/coder/code-server).
117118
- [**Setup Coder**](https://github.com/marketplace/actions/setup-coder): An action to setup coder CLI in GitHub workflows.

agent/agent.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ type Options struct {
9595
}
9696

9797
type Client interface {
98-
ConnectRPC25(ctx context.Context) (
99-
proto.DRPCAgentClient25, tailnetproto.DRPCTailnetClient25, error,
98+
ConnectRPC26(ctx context.Context) (
99+
proto.DRPCAgentClient26, tailnetproto.DRPCTailnetClient26, error,
100100
)
101101
RewriteDERPMap(derpMap *tailcfg.DERPMap)
102102
}
@@ -908,7 +908,7 @@ func (a *agent) run() (retErr error) {
908908
a.sessionToken.Store(&sessionToken)
909909

910910
// ConnectRPC returns the dRPC connection we use for the Agent and Tailnet v2+ APIs
911-
aAPI, tAPI, err := a.client.ConnectRPC25(a.hardCtx)
911+
aAPI, tAPI, err := a.client.ConnectRPC26(a.hardCtx)
912912
if err != nil {
913913
return err
914914
}

agent/agent_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,7 @@ func TestAgent_DevcontainerRecreate(t *testing.T) {
22262226
// devcontainer, we do it in a goroutine so we can process logs
22272227
// concurrently.
22282228
go func(container codersdk.WorkspaceAgentContainer) {
2229-
err := conn.RecreateDevcontainer(ctx, container.ID)
2229+
_, err := conn.RecreateDevcontainer(ctx, container.ID)
22302230
assert.NoError(t, err, "recreate devcontainer should succeed")
22312231
}(container)
22322232

agent/agentcontainers/api.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
403403
// Check if the container is running and update the known devcontainers.
404404
for i := range updated.Containers {
405405
container := &updated.Containers[i] // Grab a reference to the container to allow mutating it.
406+
container.DevcontainerStatus = "" // Reset the status for the container (updated later).
406407
container.DevcontainerDirty = false // Reset dirty state for the container (updated later).
407408

408409
workspaceFolder := container.Labels[DevcontainerLocalFolderLabel]
@@ -465,16 +466,25 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
465466
for _, dc := range api.knownDevcontainers {
466467
switch {
467468
case dc.Status == codersdk.WorkspaceAgentDevcontainerStatusStarting:
469+
if dc.Container != nil {
470+
dc.Container.DevcontainerStatus = dc.Status
471+
dc.Container.DevcontainerDirty = dc.Dirty
472+
}
468473
continue // This state is handled by the recreation routine.
469474

470475
case dc.Status == codersdk.WorkspaceAgentDevcontainerStatusError && (dc.Container == nil || dc.Container.CreatedAt.Before(api.recreateErrorTimes[dc.WorkspaceFolder])):
476+
if dc.Container != nil {
477+
dc.Container.DevcontainerStatus = dc.Status
478+
dc.Container.DevcontainerDirty = dc.Dirty
479+
}
471480
continue // The devcontainer needs to be recreated.
472481

473482
case dc.Container != nil:
474483
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStopped
475484
if dc.Container.Running {
476485
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusRunning
477486
}
487+
dc.Container.DevcontainerStatus = dc.Status
478488

479489
dc.Dirty = false
480490
if lastModified, hasModTime := api.configFileModifiedTimes[dc.ConfigPath]; hasModTime && dc.Container.CreatedAt.Before(lastModified) {
@@ -608,6 +618,9 @@ func (api *API) handleDevcontainerRecreate(w http.ResponseWriter, r *http.Reques
608618
// Update the status so that we don't try to recreate the
609619
// devcontainer multiple times in parallel.
610620
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
621+
if dc.Container != nil {
622+
dc.Container.DevcontainerStatus = dc.Status
623+
}
611624
api.knownDevcontainers[dc.WorkspaceFolder] = dc
612625
api.recreateWg.Add(1)
613626
go api.recreateDevcontainer(dc, configPath)
@@ -680,6 +693,9 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
680693
api.mu.Lock()
681694
dc = api.knownDevcontainers[dc.WorkspaceFolder]
682695
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusError
696+
if dc.Container != nil {
697+
dc.Container.DevcontainerStatus = dc.Status
698+
}
683699
api.knownDevcontainers[dc.WorkspaceFolder] = dc
684700
api.recreateErrorTimes[dc.WorkspaceFolder] = api.clock.Now("recreate", "errorTimes")
685701
api.mu.Unlock()
@@ -695,10 +711,12 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
695711
// allows the update routine to update the devcontainer status, but
696712
// to minimize the time between API consistency, we guess the status
697713
// based on the container state.
698-
if dc.Container != nil && dc.Container.Running {
699-
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusRunning
700-
} else {
701-
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStopped
714+
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStopped
715+
if dc.Container != nil {
716+
if dc.Container.Running {
717+
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusRunning
718+
}
719+
dc.Container.DevcontainerStatus = dc.Status
702720
}
703721
dc.Dirty = false
704722
api.recreateSuccessTimes[dc.WorkspaceFolder] = api.clock.Now("recreate", "successTimes")

agent/agentcontainers/api_test.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ func TestAPI(t *testing.T) {
477477
require.NoError(t, err, "unmarshal response failed")
478478
require.Len(t, resp.Devcontainers, 1, "expected one devcontainer in response")
479479
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStarting, resp.Devcontainers[0].Status, "devcontainer is not starting")
480+
require.NotNil(t, resp.Devcontainers[0].Container, "devcontainer should have container reference")
481+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStarting, resp.Devcontainers[0].Container.DevcontainerStatus, "container dc status is not starting")
480482

481483
// Allow the devcontainer CLI to continue the up process.
482484
close(tt.devcontainerCLI.continueUp)
@@ -503,6 +505,8 @@ func TestAPI(t *testing.T) {
503505
require.NoError(t, err, "unmarshal response failed after error")
504506
require.Len(t, resp.Devcontainers, 1, "expected one devcontainer in response after error")
505507
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusError, resp.Devcontainers[0].Status, "devcontainer is not in an error state after up failure")
508+
require.NotNil(t, resp.Devcontainers[0].Container, "devcontainer should have container reference after up failure")
509+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusError, resp.Devcontainers[0].Container.DevcontainerStatus, "container dc status is not error after up failure")
506510
return
507511
}
508512

@@ -525,7 +529,9 @@ func TestAPI(t *testing.T) {
525529
err = json.NewDecoder(rec.Body).Decode(&resp)
526530
require.NoError(t, err, "unmarshal response failed after recreation")
527531
require.Len(t, resp.Devcontainers, 1, "expected one devcontainer in response after recreation")
528-
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, resp.Devcontainers[0].Status, "devcontainer is not stopped after recreation")
532+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, resp.Devcontainers[0].Status, "devcontainer is not running after recreation")
533+
require.NotNil(t, resp.Devcontainers[0].Container, "devcontainer should have container reference after recreation")
534+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, resp.Devcontainers[0].Container.DevcontainerStatus, "container dc status is not running after recreation")
529535
})
530536
}
531537
})
@@ -620,6 +626,7 @@ func TestAPI(t *testing.T) {
620626
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, dc.Status)
621627
require.NotNil(t, dc.Container)
622628
assert.Equal(t, "runtime-container-1", dc.Container.ID)
629+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, dc.Container.DevcontainerStatus)
623630
},
624631
},
625632
{
@@ -660,12 +667,14 @@ func TestAPI(t *testing.T) {
660667
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStopped, known2.Status)
661668
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, runtime1.Status)
662669

663-
require.NotNil(t, known1.Container)
664670
assert.Nil(t, known2.Container)
665-
require.NotNil(t, runtime1.Container)
666671

672+
require.NotNil(t, known1.Container)
667673
assert.Equal(t, "known-container-1", known1.Container.ID)
674+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, known1.Container.DevcontainerStatus)
675+
require.NotNil(t, runtime1.Container)
668676
assert.Equal(t, "runtime-container-1", runtime1.Container.ID)
677+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, runtime1.Container.DevcontainerStatus)
669678
},
670679
},
671680
{
@@ -704,10 +713,12 @@ func TestAPI(t *testing.T) {
704713
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStopped, nonRunning.Status)
705714

706715
require.NotNil(t, running.Container, "running container should have container reference")
707-
require.NotNil(t, nonRunning.Container, "non-running container should have container reference")
708-
709716
assert.Equal(t, "running-container", running.Container.ID)
717+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, running.Container.DevcontainerStatus)
718+
719+
require.NotNil(t, nonRunning.Container, "non-running container should have container reference")
710720
assert.Equal(t, "non-running-container", nonRunning.Container.ID)
721+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusStopped, nonRunning.Container.DevcontainerStatus)
711722
},
712723
},
713724
{
@@ -743,6 +754,7 @@ func TestAPI(t *testing.T) {
743754
assert.NotEmpty(t, dc2.ConfigPath)
744755
require.NotNil(t, dc2.Container)
745756
assert.Equal(t, "known-container-2", dc2.Container.ID)
757+
assert.Equal(t, codersdk.WorkspaceAgentDevcontainerStatusRunning, dc2.Container.DevcontainerStatus)
746758
},
747759
},
748760
{
@@ -811,9 +823,14 @@ func TestAPI(t *testing.T) {
811823

812824
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
813825

826+
mClock := quartz.NewMock(t)
827+
mClock.Set(time.Now()).MustWait(testutil.Context(t, testutil.WaitShort))
828+
tickerTrap := mClock.Trap().TickerFunc("updaterLoop")
829+
814830
// Setup router with the handler under test.
815831
r := chi.NewRouter()
816832
apiOptions := []agentcontainers.Option{
833+
agentcontainers.WithClock(mClock),
817834
agentcontainers.WithLister(tt.lister),
818835
agentcontainers.WithWatcher(watcher.NewNoop()),
819836
}
@@ -838,6 +855,15 @@ func TestAPI(t *testing.T) {
838855

839856
ctx := testutil.Context(t, testutil.WaitShort)
840857

858+
// Make sure the ticker function has been registered
859+
// before advancing the clock.
860+
tickerTrap.MustWait(ctx).MustRelease(ctx)
861+
tickerTrap.Close()
862+
863+
// Advance the clock to run the updater loop.
864+
_, aw := mClock.AdvanceNext()
865+
aw.MustWait(ctx)
866+
841867
req := httptest.NewRequest(http.MethodGet, "/devcontainers", nil).
842868
WithContext(ctx)
843869
rec := httptest.NewRecorder()

agent/agentscripts/agentscripts_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ func TestScriptReportsTiming(t *testing.T) {
144144

145145
timing := timings[0]
146146
require.Equal(t, int32(0), timing.ExitCode)
147+
if assert.True(t, timing.Start.IsValid(), "start time should be valid") {
148+
require.NotZero(t, timing.Start.AsTime(), "start time should not be zero")
149+
}
150+
if assert.True(t, timing.End.IsValid(), "end time should be valid") {
151+
require.NotZero(t, timing.End.AsTime(), "end time should not be zero")
152+
}
147153
require.GreaterOrEqual(t, timing.End.AsTime(), timing.Start.AsTime())
148154
}
149155

agent/agenttest/client.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ func (c *Client) Close() {
9898
c.derpMapOnce.Do(func() { close(c.derpMapUpdates) })
9999
}
100100

101-
func (c *Client) ConnectRPC25(ctx context.Context) (
102-
agentproto.DRPCAgentClient25, proto.DRPCTailnetClient25, error,
101+
func (c *Client) ConnectRPC26(ctx context.Context) (
102+
agentproto.DRPCAgentClient26, proto.DRPCTailnetClient26, error,
103103
) {
104104
conn, lis := drpcsdk.MemTransportPipe()
105105
c.LastWorkspaceAgent = func() {
@@ -365,6 +365,18 @@ func (f *FakeAgentAPI) GetConnectionReports() []*agentproto.ReportConnectionRequ
365365
return slices.Clone(f.connectionReports)
366366
}
367367

368+
func (*FakeAgentAPI) CreateSubAgent(_ context.Context, _ *agentproto.CreateSubAgentRequest) (*agentproto.CreateSubAgentResponse, error) {
369+
panic("unimplemented")
370+
}
371+
372+
func (*FakeAgentAPI) DeleteSubAgent(_ context.Context, _ *agentproto.DeleteSubAgentRequest) (*agentproto.DeleteSubAgentResponse, error) {
373+
panic("unimplemented")
374+
}
375+
376+
func (*FakeAgentAPI) ListSubAgents(_ context.Context, _ *agentproto.ListSubAgentsRequest) (*agentproto.ListSubAgentsResponse, error) {
377+
panic("unimplemented")
378+
}
379+
368380
func NewFakeAgentAPI(t testing.TB, logger slog.Logger, manifest *agentproto.Manifest, statsCh chan *agentproto.Stats) *FakeAgentAPI {
369381
return &FakeAgentAPI{
370382
t: t,

0 commit comments

Comments
 (0)