feat(agent): populate subagent ID for terraform-defined devcontainers (#21942)

Completes the final piece of the puzzle. Support the pre-creation flow
from the agent side.
This commit is contained in:
Danielle Maywood
2026-02-06 15:52:54 +00:00
committed by GitHub
parent a5bc0eb37d
commit 6ccd20d45f
18 changed files with 869 additions and 37 deletions
+15
View File
@@ -425,11 +425,20 @@ func DevcontainerFromProto(pdc *proto.WorkspaceAgentDevcontainer) (codersdk.Work
if err != nil {
return codersdk.WorkspaceAgentDevcontainer{}, xerrors.Errorf("parse id: %w", err)
}
var subagentID uuid.NullUUID
if pdc.SubagentId != nil {
subagentID.Valid = true
subagentID.UUID, err = uuid.FromBytes(pdc.SubagentId)
if err != nil {
return codersdk.WorkspaceAgentDevcontainer{}, xerrors.Errorf("parse subagent id: %w", err)
}
}
return codersdk.WorkspaceAgentDevcontainer{
ID: id,
Name: pdc.Name,
WorkspaceFolder: pdc.WorkspaceFolder,
ConfigPath: pdc.ConfigPath,
SubagentID: subagentID,
}, nil
}
@@ -442,10 +451,16 @@ func ProtoFromDevcontainers(dcs []codersdk.WorkspaceAgentDevcontainer) []*proto.
}
func ProtoFromDevcontainer(dc codersdk.WorkspaceAgentDevcontainer) *proto.WorkspaceAgentDevcontainer {
var subagentID []byte
if dc.SubagentID.Valid {
subagentID = dc.SubagentID.UUID[:]
}
return &proto.WorkspaceAgentDevcontainer{
Id: dc.ID[:],
Name: dc.Name,
WorkspaceFolder: dc.WorkspaceFolder,
ConfigPath: dc.ConfigPath,
SubagentId: subagentID,
}
}
+1
View File
@@ -136,6 +136,7 @@ func TestManifest(t *testing.T) {
ID: uuid.New(),
WorkspaceFolder: "/home/coder/coder",
ConfigPath: "/home/coder/coder/.devcontainer/devcontainer.json",
SubagentID: uuid.NullUUID{Valid: true, UUID: uuid.New()},
},
},
}
+12 -4
View File
@@ -440,10 +440,11 @@ func (s WorkspaceAgentDevcontainerStatus) Transitioning() bool {
// WorkspaceAgentDevcontainer defines the location of a devcontainer
// configuration in a workspace that is visible to the workspace agent.
type WorkspaceAgentDevcontainer struct {
ID uuid.UUID `json:"id" format:"uuid"`
Name string `json:"name"`
WorkspaceFolder string `json:"workspace_folder"`
ConfigPath string `json:"config_path,omitempty"`
ID uuid.UUID `json:"id" format:"uuid"`
Name string `json:"name"`
WorkspaceFolder string `json:"workspace_folder"`
ConfigPath string `json:"config_path,omitempty"`
SubagentID uuid.NullUUID `json:"subagent_id,omitempty" format:"uuid"`
// Additional runtime fields.
Status WorkspaceAgentDevcontainerStatus `json:"status"`
@@ -458,6 +459,7 @@ func (d WorkspaceAgentDevcontainer) Equals(other WorkspaceAgentDevcontainer) boo
return d.ID == other.ID &&
d.Name == other.Name &&
d.WorkspaceFolder == other.WorkspaceFolder &&
d.SubagentID == other.SubagentID &&
d.Status == other.Status &&
d.Dirty == other.Dirty &&
(d.Container == nil && other.Container == nil ||
@@ -467,6 +469,12 @@ func (d WorkspaceAgentDevcontainer) Equals(other WorkspaceAgentDevcontainer) boo
d.Error == other.Error
}
// IsTerraformDefined returns true if this devcontainer has resources defined
// in Terraform.
func (d WorkspaceAgentDevcontainer) IsTerraformDefined() bool {
return d.SubagentID.Valid
}
// WorkspaceAgentDevcontainerAgent represents the sub agent for a
// devcontainer.
type WorkspaceAgentDevcontainerAgent struct {
+139
View File
@@ -110,3 +110,142 @@ func TestWorkspaceAgentLogTextSpecialChars(t *testing.T) {
result := log.Text("main", "startup_script")
require.Equal(t, "2024-01-28T10:30:00Z [debug] [agent.main|startup_script] \033[31mError!\033[0m 🚀 Unicode: 日本語", result)
}
func TestWorkspaceAgentDevcontainerEquals(t *testing.T) {
t.Parallel()
agentID := uuid.New()
base := codersdk.WorkspaceAgentDevcontainer{
ID: uuid.New(),
Name: "test-dc",
WorkspaceFolder: "/workspace",
Status: codersdk.WorkspaceAgentDevcontainerStatusRunning,
Dirty: false,
Container: &codersdk.WorkspaceAgentContainer{ID: "container-123"},
Agent: &codersdk.WorkspaceAgentDevcontainerAgent{ID: agentID, Name: "agent-1"},
Error: "",
}
tests := []struct {
name string
modify func(*codersdk.WorkspaceAgentDevcontainer)
wantEqual bool
}{
{
name: "identical",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) {},
wantEqual: true,
},
{
name: "different ID",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.ID = uuid.New() },
wantEqual: false,
},
{
name: "different Name",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.Name = "other-dc" },
wantEqual: false,
},
{
name: "different WorkspaceFolder",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.WorkspaceFolder = "/other" },
wantEqual: false,
},
{
name: "different SubagentID (one valid, one nil)",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) {
d.SubagentID = uuid.NullUUID{Valid: true, UUID: uuid.New()}
},
wantEqual: false,
},
{
name: "different SubagentID UUIDs",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) {
d.SubagentID = uuid.NullUUID{Valid: true, UUID: uuid.New()}
},
wantEqual: false,
},
{
name: "different Status",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) {
d.Status = codersdk.WorkspaceAgentDevcontainerStatusStopped
},
wantEqual: false,
},
{
name: "different Dirty",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.Dirty = true },
wantEqual: false,
},
{
name: "different Container (one nil)",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.Container = nil },
wantEqual: false,
},
{
name: "different Container IDs",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) {
d.Container = &codersdk.WorkspaceAgentContainer{ID: "different-container"}
},
wantEqual: false,
},
{
name: "different Agent (one nil)",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.Agent = nil },
wantEqual: false,
},
{
name: "different Agent values",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) {
d.Agent = &codersdk.WorkspaceAgentDevcontainerAgent{ID: agentID, Name: "agent-2"}
},
wantEqual: false,
},
{
name: "different Error",
modify: func(d *codersdk.WorkspaceAgentDevcontainer) { d.Error = "some error" },
wantEqual: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
modified := base
tt.modify(&modified)
require.Equal(t, tt.wantEqual, base.Equals(modified))
})
}
}
func TestWorkspaceAgentDevcontainerIsTerraformDefined(t *testing.T) {
t.Parallel()
t.Run("SubagentID Valid", func(t *testing.T) {
t.Parallel()
dc := codersdk.WorkspaceAgentDevcontainer{
ID: uuid.New(),
Name: "test-dc",
WorkspaceFolder: "/workspace",
SubagentID: uuid.NullUUID{Valid: true, UUID: uuid.New()},
}
require.True(t, dc.IsTerraformDefined())
})
t.Run("SubagentID Null", func(t *testing.T) {
t.Parallel()
dc := codersdk.WorkspaceAgentDevcontainer{
ID: uuid.New(),
Name: "test-dc",
WorkspaceFolder: "/workspace",
SubagentID: uuid.NullUUID{Valid: false},
}
require.False(t, dc.IsTerraformDefined())
})
}