Thanks to visit codestin.com
Credit goes to github.com

Skip to content
6 changes: 4 additions & 2 deletions coderd/database/dbfake/dbfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,10 @@ func (b WorkspaceBuildBuilder) doInTX() WorkspaceResponse {

workspaceAgentID := uuid.NullUUID{}
workspaceAppID := uuid.NullUUID{}
// Workspace agent and app are only properly set upon job completion
if b.jobStatus != database.ProvisionerJobStatusPending && b.jobStatus != database.ProvisionerJobStatusRunning {
// Workspace agent and app are only properly set upon job completion, and
// only start builds have agents.
isStart := b.seed.Transition == "" || b.seed.Transition == database.WorkspaceTransitionStart
if isStart && b.jobStatus != database.ProvisionerJobStatusPending && b.jobStatus != database.ProvisionerJobStatusRunning {
app := mustWorkspaceAppByWorkspaceAndBuildAndAppID(ownerCtx, b.t, b.db, resp.Workspace.ID, resp.Build.BuildNumber, b.taskAppID)
workspaceAgentID = uuid.NullUUID{UUID: app.AgentID, Valid: true}
workspaceAppID = uuid.NullUUID{UUID: app.ID, Valid: true}
Expand Down
13 changes: 11 additions & 2 deletions coderd/provisionerdserver/provisionerdserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2345,7 +2345,8 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
taskAppID uuid.NullUUID
taskAgentID uuid.NullUUID
)
if tasks := jobType.WorkspaceBuild.GetAiTasks(); len(tasks) > 0 {
// Agents and their apps are only inserted when the workspace is running.
if tasks := jobType.WorkspaceBuild.GetAiTasks(); len(tasks) > 0 && workspaceBuild.Transition == database.WorkspaceTransitionStart {
task := tasks[0]
if task == nil {
return xerrors.Errorf("update ai task: task is nil")
Expand Down Expand Up @@ -3035,7 +3036,15 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
agentNames = make(map[string]struct{})
appSlugs = make(map[string]struct{})
)
for _, prAgent := range protoResource.Agents {

// Agents can't connect to compute that these transitions tore down, so any
// agent Terraform still reports for them would only surface as unhealthy.
protoAgents := protoResource.Agents
if transition == database.WorkspaceTransitionStop || transition == database.WorkspaceTransitionDelete {
protoAgents = nil
}

for _, prAgent := range protoAgents {
// Similar logic is duplicated in terraform/resources.go.
if prAgent.Name == "" {
return xerrors.Errorf("agent name cannot be empty")
Expand Down
3 changes: 2 additions & 1 deletion coderd/provisionerdserver/provisionerdserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3608,7 +3608,8 @@ func TestCompleteJob(t *testing.T) {
},
isTask: true,
expectTaskStatus: database.TaskStatusPaused,
expectAppID: uuid.NullUUID{UUID: sidebarAppID, Valid: true},
// Stop builds don't create agents or apps.
expectAppID: uuid.NullUUID{},
expectHasAiTask: true,
expectUsageEvent: false,
},
Expand Down
20 changes: 17 additions & 3 deletions coderd/templateversions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/coder/coder/v2/coderd/provisionerdserver"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/examples"
"github.com/coder/coder/v2/provisioner/echo"
Expand Down Expand Up @@ -1197,10 +1198,23 @@ func TestTemplateVersionResources(t *testing.T) {
resources, err := client.TemplateVersionResources(ctx, version.ID)
require.NoError(t, err)
require.NotNil(t, resources)
// An import plans both transitions, so every resource is recorded twice.
// Resources are only sorted by name, and the two "some" rows tie, so
// they're matched on transition rather than by index.
require.Len(t, resources, 4)
require.Equal(t, "some", resources[2].Name)
require.Equal(t, "example", resources[2].Type)
require.Len(t, resources[2].Agents, 1)
start, ok := slice.Find(resources, func(r codersdk.WorkspaceResource) bool {
return r.Name == "some" && r.Transition == codersdk.WorkspaceTransitionStart
})
require.True(t, ok)
require.Equal(t, "example", start.Type)
require.Len(t, start.Agents, 1)

stop, ok := slice.Find(resources, func(r codersdk.WorkspaceResource) bool {
return r.Name == "some" && r.Transition == codersdk.WorkspaceTransitionStop
})
require.True(t, ok)
// A stopped workspace has no agents, so the stop plan doesn't report any.
require.Empty(t, stop.Agents)
})
}

Expand Down
35 changes: 35 additions & 0 deletions coderd/workspacebuilds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,41 @@ func TestWorkspaceBuildResources(t *testing.T) {
assertWorkspaceResource(t, workspace.LatestBuild.Resources[3], "fourth_resource", "example", 0) // resource has no agents, sorted by name
assertWorkspaceResource(t, workspace.LatestBuild.Resources[4], "third_resource", "example", 0) // resource is the last one
})
t.Run("StopBuildHasNoAgents", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
user := coderdtest.CreateFirstUser(t, client)
// The same responses are replayed for every transition, mimicking a
// template where the agent is bound to a resource that persists across
// stop.
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionGraph: echo.ProvisionGraphWithAgent(uuid.NewString()),
})
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)

ctx := testutil.Context(t, testutil.WaitLong)

workspace, err := client.Workspace(ctx, workspace.ID)
require.NoError(t, err)
require.Len(t, workspace.LatestBuild.Resources, 1)
require.Len(t, workspace.LatestBuild.Resources[0].Agents, 1)

build := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID)

workspace, err = client.Workspace(ctx, workspace.ID)
require.NoError(t, err)
// The resource is still reported, but a stopped workspace has no agents
// and so cannot be unhealthy because of one.
require.Len(t, workspace.LatestBuild.Resources, 1)
require.Empty(t, workspace.LatestBuild.Resources[0].Agents)
require.True(t, workspace.Health.Healthy)
require.Empty(t, workspace.Health.FailingAgents)
})
}

func TestWorkspaceBuildWithUpdatedTemplateVersionSendsNotification(t *testing.T) {
Expand Down
Loading