Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f14b590254 | |||
| 83e0e0b5df | |||
| 5e09b91cbc | |||
| ad1dddb309 |
@@ -402,6 +402,7 @@ func WorkspaceAgentDevcontainer(t testing.TB, db database.Store, orig database.W
|
||||
Name: []string{takeFirst(orig.Name, testutil.GetRandomName(t))},
|
||||
WorkspaceFolder: []string{takeFirst(orig.WorkspaceFolder, "/workspace")},
|
||||
ConfigPath: []string{takeFirst(orig.ConfigPath, "")},
|
||||
SubagentID: []uuid.UUID{takeFirst(orig.SubagentID, uuid.NullUUID{}).UUID},
|
||||
})
|
||||
require.NoError(t, err, "insert workspace agent devcontainer")
|
||||
return devcontainers[0]
|
||||
|
||||
Generated
+2
-1
@@ -2505,7 +2505,8 @@ CREATE TABLE workspace_agent_devcontainers (
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
workspace_folder text NOT NULL,
|
||||
config_path text NOT NULL,
|
||||
name text NOT NULL
|
||||
name text NOT NULL,
|
||||
subagent_id uuid
|
||||
);
|
||||
|
||||
COMMENT ON TABLE workspace_agent_devcontainers IS 'Workspace agent devcontainer configuration';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE workspace_agent_devcontainers
|
||||
DROP COLUMN subagent_id;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE workspace_agent_devcontainers
|
||||
ADD COLUMN subagent_id UUID;
|
||||
@@ -4743,7 +4743,8 @@ type WorkspaceAgentDevcontainer struct {
|
||||
// Path to devcontainer.json.
|
||||
ConfigPath string `db:"config_path" json:"config_path"`
|
||||
// The name of the Dev Container.
|
||||
Name string `db:"name" json:"name"`
|
||||
Name string `db:"name" json:"name"`
|
||||
SubagentID uuid.NullUUID `db:"subagent_id" json:"subagent_id"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentLog struct {
|
||||
|
||||
@@ -7989,3 +7989,99 @@ func TestDeleteExpiredAPIKeys(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, remaining, len(unexpiredTimes))
|
||||
}
|
||||
|
||||
func TestWorkspaceAgentDevcontainersSubagentID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
|
||||
// Setup: create workspace agent
|
||||
org := dbgen.Organization(t, db, database.Organization{})
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
tpl := dbgen.Template(t, db, database.Template{
|
||||
OrganizationID: org.ID,
|
||||
CreatedBy: user.ID,
|
||||
})
|
||||
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
OrganizationID: org.ID,
|
||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||
CreatedBy: user.ID,
|
||||
})
|
||||
ws := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
OrganizationID: org.ID,
|
||||
OwnerID: user.ID,
|
||||
TemplateID: tpl.ID,
|
||||
})
|
||||
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||
OrganizationID: org.ID,
|
||||
})
|
||||
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
WorkspaceID: ws.ID,
|
||||
JobID: job.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
})
|
||||
res := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
|
||||
JobID: build.JobID,
|
||||
})
|
||||
agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
|
||||
ResourceID: res.ID,
|
||||
})
|
||||
|
||||
// Create a subagent that will be referenced
|
||||
subagent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
|
||||
ResourceID: res.ID,
|
||||
ParentID: uuid.NullUUID{UUID: agent.ID, Valid: true},
|
||||
})
|
||||
|
||||
t.Run("InsertWithSubagentID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
devcontainers, err := db.InsertWorkspaceAgentDevcontainers(ctx, database.InsertWorkspaceAgentDevcontainersParams{
|
||||
WorkspaceAgentID: agent.ID,
|
||||
CreatedAt: dbtime.Now(),
|
||||
ID: []uuid.UUID{uuid.New()},
|
||||
Name: []string{"test-devcontainer"},
|
||||
WorkspaceFolder: []string{"/workspace"},
|
||||
ConfigPath: []string{"/workspace/.devcontainer/devcontainer.json"},
|
||||
SubagentID: []uuid.UUID{subagent.ID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, devcontainers, 1)
|
||||
require.True(t, devcontainers[0].SubagentID.Valid)
|
||||
require.Equal(t, subagent.ID, devcontainers[0].SubagentID.UUID)
|
||||
|
||||
// Verify retrieval
|
||||
retrieved, err := db.GetWorkspaceAgentDevcontainersByAgentID(ctx, agent.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, retrieved, 1)
|
||||
require.True(t, retrieved[0].SubagentID.Valid)
|
||||
require.Equal(t, subagent.ID, retrieved[0].SubagentID.UUID)
|
||||
})
|
||||
|
||||
t.Run("InsertWithNilSubagentID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
// Create a separate agent for this subtest
|
||||
agent2 := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
|
||||
ResourceID: res.ID,
|
||||
})
|
||||
|
||||
// When uuid.Nil is passed, it stores the zero UUID (not NULL).
|
||||
// This matches the provisionerdserver behavior.
|
||||
devcontainers, err := db.InsertWorkspaceAgentDevcontainers(ctx, database.InsertWorkspaceAgentDevcontainersParams{
|
||||
WorkspaceAgentID: agent2.ID,
|
||||
CreatedAt: dbtime.Now(),
|
||||
ID: []uuid.UUID{uuid.New()},
|
||||
Name: []string{"no-subagent"},
|
||||
WorkspaceFolder: []string{"/workspace"},
|
||||
ConfigPath: []string{""},
|
||||
SubagentID: []uuid.UUID{uuid.Nil},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, devcontainers, 1)
|
||||
// uuid.Nil is stored as a zero UUID, not NULL.
|
||||
require.Equal(t, uuid.Nil, devcontainers[0].SubagentID.UUID)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17336,7 +17336,7 @@ func (q *sqlQuerier) ValidateUserIDs(ctx context.Context, userIds []uuid.UUID) (
|
||||
|
||||
const getWorkspaceAgentDevcontainersByAgentID = `-- name: GetWorkspaceAgentDevcontainersByAgentID :many
|
||||
SELECT
|
||||
id, workspace_agent_id, created_at, workspace_folder, config_path, name
|
||||
id, workspace_agent_id, created_at, workspace_folder, config_path, name, subagent_id
|
||||
FROM
|
||||
workspace_agent_devcontainers
|
||||
WHERE
|
||||
@@ -17361,6 +17361,7 @@ func (q *sqlQuerier) GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context
|
||||
&i.WorkspaceFolder,
|
||||
&i.ConfigPath,
|
||||
&i.Name,
|
||||
&i.SubagentID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -17377,15 +17378,16 @@ func (q *sqlQuerier) GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context
|
||||
|
||||
const insertWorkspaceAgentDevcontainers = `-- name: InsertWorkspaceAgentDevcontainers :many
|
||||
INSERT INTO
|
||||
workspace_agent_devcontainers (workspace_agent_id, created_at, id, name, workspace_folder, config_path)
|
||||
workspace_agent_devcontainers (workspace_agent_id, created_at, id, name, workspace_folder, config_path, subagent_id)
|
||||
SELECT
|
||||
$1::uuid AS workspace_agent_id,
|
||||
$2::timestamptz AS created_at,
|
||||
unnest($3::uuid[]) AS id,
|
||||
unnest($4::text[]) AS name,
|
||||
unnest($5::text[]) AS workspace_folder,
|
||||
unnest($6::text[]) AS config_path
|
||||
RETURNING workspace_agent_devcontainers.id, workspace_agent_devcontainers.workspace_agent_id, workspace_agent_devcontainers.created_at, workspace_agent_devcontainers.workspace_folder, workspace_agent_devcontainers.config_path, workspace_agent_devcontainers.name
|
||||
unnest($6::text[]) AS config_path,
|
||||
unnest($7::uuid[]) AS subagent_id
|
||||
RETURNING workspace_agent_devcontainers.id, workspace_agent_devcontainers.workspace_agent_id, workspace_agent_devcontainers.created_at, workspace_agent_devcontainers.workspace_folder, workspace_agent_devcontainers.config_path, workspace_agent_devcontainers.name, workspace_agent_devcontainers.subagent_id
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentDevcontainersParams struct {
|
||||
@@ -17395,6 +17397,7 @@ type InsertWorkspaceAgentDevcontainersParams struct {
|
||||
Name []string `db:"name" json:"name"`
|
||||
WorkspaceFolder []string `db:"workspace_folder" json:"workspace_folder"`
|
||||
ConfigPath []string `db:"config_path" json:"config_path"`
|
||||
SubagentID []uuid.UUID `db:"subagent_id" json:"subagent_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg InsertWorkspaceAgentDevcontainersParams) ([]WorkspaceAgentDevcontainer, error) {
|
||||
@@ -17405,6 +17408,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg
|
||||
pq.Array(arg.Name),
|
||||
pq.Array(arg.WorkspaceFolder),
|
||||
pq.Array(arg.ConfigPath),
|
||||
pq.Array(arg.SubagentID),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -17420,6 +17424,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg
|
||||
&i.WorkspaceFolder,
|
||||
&i.ConfigPath,
|
||||
&i.Name,
|
||||
&i.SubagentID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
-- name: InsertWorkspaceAgentDevcontainers :many
|
||||
INSERT INTO
|
||||
workspace_agent_devcontainers (workspace_agent_id, created_at, id, name, workspace_folder, config_path)
|
||||
workspace_agent_devcontainers (workspace_agent_id, created_at, id, name, workspace_folder, config_path, subagent_id)
|
||||
SELECT
|
||||
@workspace_agent_id::uuid AS workspace_agent_id,
|
||||
@created_at::timestamptz AS created_at,
|
||||
unnest(@id::uuid[]) AS id,
|
||||
unnest(@name::text[]) AS name,
|
||||
unnest(@workspace_folder::text[]) AS workspace_folder,
|
||||
unnest(@config_path::text[]) AS config_path
|
||||
unnest(@config_path::text[]) AS config_path,
|
||||
unnest(@subagent_id::uuid[]) AS subagent_id
|
||||
RETURNING workspace_agent_devcontainers.*;
|
||||
|
||||
-- name: GetWorkspaceAgentDevcontainersByAgentID :many
|
||||
|
||||
@@ -2897,6 +2897,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
devcontainerNames = make([]string, 0, len(devcontainers))
|
||||
devcontainerWorkspaceFolders = make([]string, 0, len(devcontainers))
|
||||
devcontainerConfigPaths = make([]string, 0, len(devcontainers))
|
||||
devcontainerSubagentIDs = make([]uuid.UUID, 0, len(devcontainers))
|
||||
)
|
||||
for _, dc := range devcontainers {
|
||||
id := uuid.New()
|
||||
@@ -2904,6 +2905,9 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
devcontainerNames = append(devcontainerNames, dc.Name)
|
||||
devcontainerWorkspaceFolders = append(devcontainerWorkspaceFolders, dc.WorkspaceFolder)
|
||||
devcontainerConfigPaths = append(devcontainerConfigPaths, dc.ConfigPath)
|
||||
var subagentID uuid.UUID
|
||||
copy(subagentID[:], dc.GetSubagentId())
|
||||
devcontainerSubagentIDs = append(devcontainerSubagentIDs, subagentID)
|
||||
|
||||
// Add a log source and script for each devcontainer so we can
|
||||
// track logs and timings for each devcontainer.
|
||||
@@ -2932,6 +2936,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
Name: devcontainerNames,
|
||||
WorkspaceFolder: devcontainerWorkspaceFolders,
|
||||
ConfigPath: devcontainerConfigPaths,
|
||||
SubagentID: devcontainerSubagentIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert agent devcontainer: %w", err)
|
||||
|
||||
@@ -3706,6 +3706,7 @@ func TestInsertWorkspaceResource(t *testing.T) {
|
||||
t.Parallel()
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{})
|
||||
subagentID := uuid.New()
|
||||
err := insert(db, job.ID, &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
@@ -3714,6 +3715,7 @@ func TestInsertWorkspaceResource(t *testing.T) {
|
||||
Devcontainers: []*sdkproto.Devcontainer{
|
||||
{Name: "foo", WorkspaceFolder: "/workspace1"},
|
||||
{Name: "bar", WorkspaceFolder: "/workspace2", ConfigPath: "/workspace2/.devcontainer/devcontainer.json"},
|
||||
{Name: "baz", WorkspaceFolder: "/workspace3", SubagentId: subagentID[:]},
|
||||
},
|
||||
}},
|
||||
})
|
||||
@@ -3730,13 +3732,17 @@ func TestInsertWorkspaceResource(t *testing.T) {
|
||||
return devcontainers[i].Name > devcontainers[j].Name
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, devcontainers, 2)
|
||||
require.Len(t, devcontainers, 3)
|
||||
require.Equal(t, "foo", devcontainers[0].Name)
|
||||
require.Equal(t, "/workspace1", devcontainers[0].WorkspaceFolder)
|
||||
require.Equal(t, "", devcontainers[0].ConfigPath)
|
||||
require.Equal(t, "bar", devcontainers[1].Name)
|
||||
require.Equal(t, "/workspace2", devcontainers[1].WorkspaceFolder)
|
||||
require.Equal(t, "/workspace2/.devcontainer/devcontainer.json", devcontainers[1].ConfigPath)
|
||||
require.Equal(t, uuid.Nil, devcontainers[0].SubagentID.UUID)
|
||||
require.Equal(t, "baz", devcontainers[1].Name)
|
||||
require.Equal(t, "/workspace3", devcontainers[1].WorkspaceFolder)
|
||||
require.Equal(t, subagentID, devcontainers[1].SubagentID.UUID)
|
||||
require.Equal(t, "bar", devcontainers[2].Name)
|
||||
require.Equal(t, "/workspace2", devcontainers[2].WorkspaceFolder)
|
||||
require.Equal(t, "/workspace2/.devcontainer/devcontainer.json", devcontainers[2].ConfigPath)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Generated
+593
-536
File diff suppressed because it is too large
Load Diff
@@ -244,6 +244,11 @@ message Devcontainer {
|
||||
string workspace_folder = 1;
|
||||
string config_path = 2;
|
||||
string name = 3;
|
||||
optional bytes id = 4;
|
||||
optional bytes subagent_id = 5;
|
||||
repeated App apps = 6;
|
||||
repeated Script scripts = 7;
|
||||
repeated Env envs = 8;
|
||||
}
|
||||
|
||||
enum AppOpenIn {
|
||||
|
||||
Generated
+20
@@ -306,6 +306,11 @@ export interface Devcontainer {
|
||||
workspaceFolder: string;
|
||||
configPath: string;
|
||||
name: string;
|
||||
id?: Uint8Array | undefined;
|
||||
subagentId?: Uint8Array | undefined;
|
||||
apps: App[];
|
||||
scripts: Script[];
|
||||
envs: Env[];
|
||||
}
|
||||
|
||||
/** App represents a dev-accessible application on the workspace. */
|
||||
@@ -1095,6 +1100,21 @@ export const Devcontainer = {
|
||||
if (message.name !== "") {
|
||||
writer.uint32(26).string(message.name);
|
||||
}
|
||||
if (message.id !== undefined) {
|
||||
writer.uint32(34).bytes(message.id);
|
||||
}
|
||||
if (message.subagentId !== undefined) {
|
||||
writer.uint32(42).bytes(message.subagentId);
|
||||
}
|
||||
for (const v of message.apps) {
|
||||
App.encode(v!, writer.uint32(50).fork()).ldelim();
|
||||
}
|
||||
for (const v of message.scripts) {
|
||||
Script.encode(v!, writer.uint32(58).fork()).ldelim();
|
||||
}
|
||||
for (const v of message.envs) {
|
||||
Env.encode(v!, writer.uint32(66).fork()).ldelim();
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user