Skip to content

Commit cfac9ee

Browse files
Merge branch 'coder:main' into main
2 parents 2e57eed + 321c2b8 commit cfac9ee

File tree

34 files changed

+395
-279
lines changed

34 files changed

+395
-279
lines changed

cli/exp_task_status_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,16 @@ STATE CHANGED STATUS STATE MESSAGE
188188
"id": "11111111-1111-1111-1111-111111111111",
189189
"organization_id": "00000000-0000-0000-0000-000000000000",
190190
"owner_id": "00000000-0000-0000-0000-000000000000",
191+
"owner_name": "",
191192
"name": "",
192193
"template_id": "00000000-0000-0000-0000-000000000000",
194+
"template_name": "",
195+
"template_display_name": "",
196+
"template_icon": "",
193197
"workspace_id": null,
198+
"workspace_agent_id": null,
199+
"workspace_agent_lifecycle": null,
200+
"workspace_agent_health": null,
194201
"initial_prompt": "",
195202
"status": "running",
196203
"current_state": {

coderd/aitasks.go

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,30 @@ func (api *API) tasksFromWorkspaces(ctx context.Context, apiWorkspaces []codersd
213213

214214
tasks := make([]codersdk.Task, 0, len(apiWorkspaces))
215215
for _, ws := range apiWorkspaces {
216+
// TODO(DanielleMaywood):
217+
// This just picks up the first agent it discovers.
218+
// This approach _might_ break when a task has multiple agents,
219+
// depending on which agent was found first.
220+
//
221+
// We explicitly do not have support for running tasks
222+
// inside of a sub agent at the moment, so we can be sure
223+
// that any sub agents are not the agent we're looking for.
224+
var taskAgentID uuid.NullUUID
225+
var taskAgentLifecycle *codersdk.WorkspaceAgentLifecycle
226+
var taskAgentHealth *codersdk.WorkspaceAgentHealth
227+
for _, resource := range ws.LatestBuild.Resources {
228+
for _, agent := range resource.Agents {
229+
if agent.ParentID.Valid {
230+
continue
231+
}
232+
233+
taskAgentID = uuid.NullUUID{Valid: true, UUID: agent.ID}
234+
taskAgentLifecycle = &agent.LifecycleState
235+
taskAgentHealth = &agent.Health
236+
break
237+
}
238+
}
239+
216240
var currentState *codersdk.TaskStateEntry
217241
if ws.LatestAppStatus != nil {
218242
currentState = &codersdk.TaskStateEntry{
@@ -222,18 +246,26 @@ func (api *API) tasksFromWorkspaces(ctx context.Context, apiWorkspaces []codersd
222246
URI: ws.LatestAppStatus.URI,
223247
}
224248
}
249+
225250
tasks = append(tasks, codersdk.Task{
226-
ID: ws.ID,
227-
OrganizationID: ws.OrganizationID,
228-
OwnerID: ws.OwnerID,
229-
Name: ws.Name,
230-
TemplateID: ws.TemplateID,
231-
WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID},
232-
CreatedAt: ws.CreatedAt,
233-
UpdatedAt: ws.UpdatedAt,
234-
InitialPrompt: promptsByBuildID[ws.LatestBuild.ID],
235-
Status: ws.LatestBuild.Status,
236-
CurrentState: currentState,
251+
ID: ws.ID,
252+
OrganizationID: ws.OrganizationID,
253+
OwnerID: ws.OwnerID,
254+
OwnerName: ws.OwnerName,
255+
Name: ws.Name,
256+
TemplateID: ws.TemplateID,
257+
TemplateName: ws.TemplateName,
258+
TemplateDisplayName: ws.TemplateDisplayName,
259+
TemplateIcon: ws.TemplateIcon,
260+
WorkspaceID: uuid.NullUUID{Valid: true, UUID: ws.ID},
261+
WorkspaceAgentID: taskAgentID,
262+
WorkspaceAgentLifecycle: taskAgentLifecycle,
263+
WorkspaceAgentHealth: taskAgentHealth,
264+
CreatedAt: ws.CreatedAt,
265+
UpdatedAt: ws.UpdatedAt,
266+
InitialPrompt: promptsByBuildID[ws.LatestBuild.ID],
267+
Status: ws.LatestBuild.Status,
268+
CurrentState: currentState,
237269
})
238270
}
239271

coderd/autobuild/lifecycle_executor_test.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"database/sql"
66
"errors"
7+
"sync"
78
"testing"
89
"time"
910

@@ -1720,19 +1721,32 @@ func TestExecutorAutostartSkipsWhenNoProvisionersAvailable(t *testing.T) {
17201721
// Stop the workspace while provisioner is available
17211722
workspace = coderdtest.MustTransitionWorkspace(t, client, workspace.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
17221723

1723-
// Wait for provisioner to be available for this specific workspace
1724-
coderdtest.MustWaitForProvisionersAvailable(t, db, workspace)
17251724
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, provisionerDaemonTags)
17261725
require.NoError(t, err, "Error getting provisioner for workspace")
17271726

1728-
daemon1Closer.Close()
1727+
var wg sync.WaitGroup
1728+
wg.Add(2)
17291729

1730-
// Ensure the provisioner is stale
1731-
staleTime := sched.Next(workspace.LatestBuild.CreatedAt).Add((-1 * provisionerdserver.StaleInterval) + -10*time.Second)
1732-
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, staleTime)
1730+
next := sched.Next(workspace.LatestBuild.CreatedAt)
1731+
go func() {
1732+
defer wg.Done()
1733+
// Ensure the provisioner is stale
1734+
staleTime := next.Add(-(provisionerdserver.StaleInterval * 2))
1735+
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, staleTime)
1736+
p, err = coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, provisionerDaemonTags)
1737+
assert.NoError(t, err, "Error getting provisioner for workspace")
1738+
assert.Eventually(t, func() bool { return p.LastSeenAt.Time.UnixNano() == staleTime.UnixNano() }, testutil.WaitMedium, testutil.IntervalFast)
1739+
}()
17331740

1734-
// Trigger autobuild
1735-
tickCh <- sched.Next(workspace.LatestBuild.CreatedAt)
1741+
go func() {
1742+
defer wg.Done()
1743+
// Ensure the provisioner is gone or stale before triggering the autobuild
1744+
coderdtest.MustWaitForProvisionersUnavailable(t, db, workspace, provisionerDaemonTags, next)
1745+
// Trigger autobuild
1746+
tickCh <- next
1747+
}()
1748+
1749+
wg.Wait()
17361750

17371751
stats := <-statsCh
17381752

@@ -1758,5 +1772,5 @@ func TestExecutorAutostartSkipsWhenNoProvisionersAvailable(t *testing.T) {
17581772
}()
17591773
stats = <-statsCh
17601774

1761-
assert.Len(t, stats.Transitions, 1, "should not create builds when no provisioners available")
1775+
assert.Len(t, stats.Transitions, 1, "should create builds when provisioners are available")
17621776
}

coderd/coderdtest/coderdtest.go

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,19 +1649,48 @@ func UpdateProvisionerLastSeenAt(t *testing.T, db database.Store, id uuid.UUID,
16491649
func MustWaitForAnyProvisioner(t *testing.T, db database.Store) {
16501650
t.Helper()
16511651
ctx := ctxWithProvisionerPermissions(testutil.Context(t, testutil.WaitShort))
1652-
require.Eventually(t, func() bool {
1652+
// testutil.Eventually(t, func)
1653+
testutil.Eventually(ctx, t, func(ctx context.Context) (done bool) {
16531654
daemons, err := db.GetProvisionerDaemons(ctx)
16541655
return err == nil && len(daemons) > 0
1655-
}, testutil.WaitShort, testutil.IntervalFast)
1656+
}, testutil.IntervalFast, "no provisioner daemons found")
1657+
}
1658+
1659+
// MustWaitForProvisionersUnavailable waits for provisioners to become unavailable for a specific workspace
1660+
func MustWaitForProvisionersUnavailable(t *testing.T, db database.Store, workspace codersdk.Workspace, tags map[string]string, checkTime time.Time) {
1661+
t.Helper()
1662+
ctx := ctxWithProvisionerPermissions(testutil.Context(t, testutil.WaitMedium))
1663+
1664+
testutil.Eventually(ctx, t, func(ctx context.Context) (done bool) {
1665+
// Use the same logic as hasValidProvisioner but expect false
1666+
provisionerDaemons, err := db.GetProvisionerDaemonsByOrganization(ctx, database.GetProvisionerDaemonsByOrganizationParams{
1667+
OrganizationID: workspace.OrganizationID,
1668+
WantTags: tags,
1669+
})
1670+
if err != nil {
1671+
return false
1672+
}
1673+
1674+
// Check if NO provisioners are active (all are stale or gone)
1675+
for _, pd := range provisionerDaemons {
1676+
if pd.LastSeenAt.Valid {
1677+
age := checkTime.Sub(pd.LastSeenAt.Time)
1678+
if age <= provisionerdserver.StaleInterval {
1679+
return false // Found an active provisioner, keep waiting
1680+
}
1681+
}
1682+
}
1683+
return true // No active provisioners found
1684+
}, testutil.IntervalFast, "there are still provisioners available for workspace, expected none")
16561685
}
16571686

16581687
// MustWaitForProvisionersAvailable waits for provisioners to be available for a specific workspace.
1659-
func MustWaitForProvisionersAvailable(t *testing.T, db database.Store, workspace codersdk.Workspace) uuid.UUID {
1688+
func MustWaitForProvisionersAvailable(t *testing.T, db database.Store, workspace codersdk.Workspace, ts time.Time) uuid.UUID {
16601689
t.Helper()
1661-
ctx := ctxWithProvisionerPermissions(testutil.Context(t, testutil.WaitShort))
1690+
ctx := ctxWithProvisionerPermissions(testutil.Context(t, testutil.WaitLong))
16621691
id := uuid.UUID{}
16631692
// Get the workspace from the database
1664-
require.Eventually(t, func() bool {
1693+
testutil.Eventually(ctx, t, func(ctx context.Context) (done bool) {
16651694
ws, err := db.GetWorkspaceByID(ctx, workspace.ID)
16661695
if err != nil {
16671696
return false
@@ -1689,18 +1718,17 @@ func MustWaitForProvisionersAvailable(t *testing.T, db database.Store, workspace
16891718
}
16901719

16911720
// Check if any provisioners are active (not stale)
1692-
now := time.Now()
16931721
for _, pd := range provisionerDaemons {
16941722
if pd.LastSeenAt.Valid {
1695-
age := now.Sub(pd.LastSeenAt.Time)
1723+
age := ts.Sub(pd.LastSeenAt.Time)
16961724
if age <= provisionerdserver.StaleInterval {
16971725
id = pd.ID
16981726
return true // Found an active provisioner
16991727
}
17001728
}
17011729
}
17021730
return false // No active provisioners found
1703-
}, testutil.WaitLong, testutil.IntervalFast)
1731+
}, testutil.IntervalFast, "no active provisioners available for workspace, expected at least one (non-stale)")
17041732

17051733
return id
17061734
}

codersdk/aitasks.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,24 @@ const (
8888
//
8989
// Experimental: This type is experimental and may change in the future.
9090
type Task struct {
91-
ID uuid.UUID `json:"id" format:"uuid" table:"id"`
92-
OrganizationID uuid.UUID `json:"organization_id" format:"uuid" table:"organization id"`
93-
OwnerID uuid.UUID `json:"owner_id" format:"uuid" table:"owner id"`
94-
Name string `json:"name" table:"name,default_sort"`
95-
TemplateID uuid.UUID `json:"template_id" format:"uuid" table:"template id"`
96-
WorkspaceID uuid.NullUUID `json:"workspace_id" format:"uuid" table:"workspace id"`
97-
InitialPrompt string `json:"initial_prompt" table:"initial prompt"`
98-
Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted" table:"status"`
99-
CurrentState *TaskStateEntry `json:"current_state" table:"cs,recursive_inline"`
100-
CreatedAt time.Time `json:"created_at" format:"date-time" table:"created at"`
101-
UpdatedAt time.Time `json:"updated_at" format:"date-time" table:"updated at"`
91+
ID uuid.UUID `json:"id" format:"uuid" table:"id"`
92+
OrganizationID uuid.UUID `json:"organization_id" format:"uuid" table:"organization id"`
93+
OwnerID uuid.UUID `json:"owner_id" format:"uuid" table:"owner id"`
94+
OwnerName string `json:"owner_name" table:"owner name"`
95+
Name string `json:"name" table:"name,default_sort"`
96+
TemplateID uuid.UUID `json:"template_id" format:"uuid" table:"template id"`
97+
TemplateName string `json:"template_name" table:"template name"`
98+
TemplateDisplayName string `json:"template_display_name" table:"template display name"`
99+
TemplateIcon string `json:"template_icon" table:"template icon"`
100+
WorkspaceID uuid.NullUUID `json:"workspace_id" format:"uuid" table:"workspace id"`
101+
WorkspaceAgentID uuid.NullUUID `json:"workspace_agent_id" format:"uuid" table:"workspace agent id"`
102+
WorkspaceAgentLifecycle *WorkspaceAgentLifecycle `json:"workspace_agent_lifecycle" table:"workspace agent lifecycle"`
103+
WorkspaceAgentHealth *WorkspaceAgentHealth `json:"workspace_agent_health" table:"workspace agent health"`
104+
InitialPrompt string `json:"initial_prompt" table:"initial prompt"`
105+
Status WorkspaceStatus `json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted" table:"status"`
106+
CurrentState *TaskStateEntry `json:"current_state" table:"cs,recursive_inline"`
107+
CreatedAt time.Time `json:"created_at" format:"date-time" table:"created at"`
108+
UpdatedAt time.Time `json:"updated_at" format:"date-time" table:"updated at"`
102109
}
103110

104111
// TaskStateEntry represents a single entry in the task's state history.

docs/about/contributing/modules.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ Use the version bump script to update versions:
369369

370370
## Get help
371371

372-
- **Examples**: Review existing modules like [`code-server`](https://registry.coder.com/modules/coder/code-server), [`git-clone`](https://registry.coder.com/modules/coder/git-clone), and [`jetbrains-gateway`](https://registry.coder.com/modules/coder/jetbrains-gateway)
372+
- **Examples**: Review existing modules like [`code-server`](https://registry.coder.com/modules/coder/code-server), [`git-clone`](https://registry.coder.com/modules/coder/git-clone), and [`jetbrains`](https://registry.coder.com/modules/coder/jetbrains)
373373
- **Issues**: Open an issue at [github.com/coder/registry](https://github.com/coder/registry/issues)
374374
- **Community**: Join the [Coder Discord](https://discord.gg/coder) for questions
375375
- **Documentation**: Check the [Coder docs](https://coder.com/docs) for help on Coder.

docs/admin/templates/extending-templates/modules.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ across templates. Some of the modules we publish are,
4444
[`vscode-web`](https://registry.coder.com/modules/coder/vscode-web)
4545
2. [`git-clone`](https://registry.coder.com/modules/coder/git-clone)
4646
3. [`dotfiles`](https://registry.coder.com/modules/coder/dotfiles)
47-
4. [`jetbrains-gateway`](https://registry.coder.com/modules/coder/jetbrains-gateway)
47+
4. [`jetbrains`](https://registry.coder.com/modules/coder/jetbrains)
4848
5. [`jfrog-oauth`](https://registry.coder.com/modules/coder/jfrog-oauth) and
4949
[`jfrog-token`](https://registry.coder.com/modules/coder/jfrog-token)
5050
6. [`vault-github`](https://registry.coder.com/modules/coder/vault-github)

dogfood/coder-envbuilder/main.tf

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,13 @@ module "code-server" {
135135
auto_install_extensions = true
136136
}
137137

138-
module "jetbrains_gateway" {
139-
source = "dev.registry.coder.com/coder/jetbrains-gateway/coder"
140-
version = "1.1.1"
141-
agent_id = coder_agent.dev.id
142-
agent_name = "dev"
143-
folder = local.repo_dir
144-
jetbrains_ides = ["GO", "WS"]
145-
default = "GO"
146-
latest = true
138+
module "jetbrains" {
139+
count = data.coder_workspace.me.start_count
140+
source = "dev.registry.coder.com/coder/jetbrains/coder"
141+
version = "~> 1.0"
142+
agent_id = coder_agent.dev.id
143+
agent_name = "dev"
144+
folder = local.repo_dir
147145
}
148146

149147
module "filebrowser" {
@@ -448,4 +446,4 @@ resource "coder_metadata" "container_info" {
448446
key = "region"
449447
value = data.coder_parameter.region.option[index(data.coder_parameter.region.option.*.value, data.coder_parameter.region.value)].name
450448
}
451-
}
449+
}

enterprise/coderd/workspaces_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,13 +2242,14 @@ func TestPrebuildsAutobuild(t *testing.T) {
22422242
workspace = coderdtest.MustTransitionWorkspace(t, client, workspace.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
22432243
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
22442244

2245+
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, nil)
2246+
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, sched.Next(prebuild.LatestBuild.CreatedAt))
2247+
22452248
// Wait for provisioner to be available for this specific workspace
2246-
coderdtest.MustWaitForProvisionersAvailable(t, db, prebuild)
2249+
coderdtest.MustWaitForProvisionersAvailable(t, db, prebuild, sched.Next(prebuild.LatestBuild.CreatedAt))
22472250

22482251
tickTime := sched.Next(prebuild.LatestBuild.CreatedAt).Add(time.Minute)
2249-
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, nil)
22502252
require.NoError(t, err)
2251-
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
22522253

22532254
// Tick at the next scheduled time after the prebuild’s LatestBuild.CreatedAt,
22542255
// since the next allowed autostart is calculated starting from that point.

examples/templates/aws-linux/main.tf

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -205,24 +205,14 @@ module "code-server" {
205205
order = 1
206206
}
207207

208-
# See https://registry.coder.com/modules/jetbrains-gateway
209-
module "jetbrains_gateway" {
210-
count = data.coder_workspace.me.start_count
211-
source = "registry.coder.com/modules/jetbrains-gateway/coder"
212-
213-
# JetBrains IDEs to make available for the user to select
214-
jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"]
215-
default = "IU"
216-
217-
# Default folder to open when starting a JetBrains IDE
218-
folder = "/home/coder"
219-
220-
# This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
221-
version = "~> 1.0"
222-
208+
# See https://registry.coder.com/modules/coder/jetbrains
209+
module "jetbrains" {
210+
count = data.coder_workspace.me.start_count
211+
source = "registry.coder.com/coder/jetbrains/coder"
212+
version = "~> 1.0"
223213
agent_id = coder_agent.dev[0].id
224214
agent_name = "dev"
225-
order = 2
215+
folder = "/home/coder"
226216
}
227217

228218
locals {
@@ -293,4 +283,4 @@ resource "coder_metadata" "workspace_info" {
293283
resource "aws_ec2_instance_state" "dev" {
294284
instance_id = aws_instance.dev.id
295285
state = data.coder_workspace.me.transition == "start" ? "running" : "stopped"
296-
}
286+
}

0 commit comments

Comments
 (0)