mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: allow specifying devcontainer on agent in terraform (#16997)
This change allows specifying devcontainers in terraform and plumbs it through to the agent via agent manifest. This will be used for autostarting devcontainers in a workspace. Depends on coder/terraform-provider-coder#368 Updates #16423
This commit is contained in:
@@ -3,6 +3,7 @@ package agentapi
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -42,11 +43,12 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
dbApps []database.WorkspaceApp
|
||||
scripts []database.WorkspaceAgentScript
|
||||
metadata []database.WorkspaceAgentMetadatum
|
||||
workspace database.Workspace
|
||||
owner database.User
|
||||
dbApps []database.WorkspaceApp
|
||||
scripts []database.WorkspaceAgentScript
|
||||
metadata []database.WorkspaceAgentMetadatum
|
||||
workspace database.Workspace
|
||||
owner database.User
|
||||
devcontainers []database.WorkspaceAgentDevcontainer
|
||||
)
|
||||
|
||||
var eg errgroup.Group
|
||||
@@ -80,6 +82,13 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
|
||||
}
|
||||
return err
|
||||
})
|
||||
eg.Go(func() (err error) {
|
||||
devcontainers, err = a.Database.GetWorkspaceAgentDevcontainersByAgentID(ctx, workspaceAgent.ID)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("fetching workspace agent data: %w", err)
|
||||
@@ -125,10 +134,11 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
|
||||
DisableDirectConnections: a.DisableDirectConnections,
|
||||
DerpForceWebsockets: a.DerpForceWebSockets,
|
||||
|
||||
DerpMap: tailnet.DERPMapToProto(a.DerpMapFn()),
|
||||
Scripts: dbAgentScriptsToProto(scripts),
|
||||
Apps: apps,
|
||||
Metadata: dbAgentMetadataToProtoDescription(metadata),
|
||||
DerpMap: tailnet.DERPMapToProto(a.DerpMapFn()),
|
||||
Scripts: dbAgentScriptsToProto(scripts),
|
||||
Apps: apps,
|
||||
Metadata: dbAgentMetadataToProtoDescription(metadata),
|
||||
Devcontainers: dbAgentDevcontainersToProto(devcontainers),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -228,3 +238,15 @@ func dbAppToProto(dbApp database.WorkspaceApp, agent database.WorkspaceAgent, ow
|
||||
Hidden: dbApp.Hidden,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func dbAgentDevcontainersToProto(devcontainers []database.WorkspaceAgentDevcontainer) []*agentproto.WorkspaceAgentDevcontainer {
|
||||
ret := make([]*agentproto.WorkspaceAgentDevcontainer, len(devcontainers))
|
||||
for i, dc := range devcontainers {
|
||||
ret[i] = &agentproto.WorkspaceAgentDevcontainer{
|
||||
Id: dc.ID[:],
|
||||
WorkspaceFolder: dc.WorkspaceFolder,
|
||||
ConfigPath: dc.ConfigPath,
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -156,6 +156,19 @@ func TestGetManifest(t *testing.T) {
|
||||
CollectedAt: someTime.Add(time.Hour),
|
||||
},
|
||||
}
|
||||
devcontainers = []database.WorkspaceAgentDevcontainer{
|
||||
{
|
||||
ID: uuid.New(),
|
||||
WorkspaceAgentID: agent.ID,
|
||||
WorkspaceFolder: "/cool/folder",
|
||||
},
|
||||
{
|
||||
ID: uuid.New(),
|
||||
WorkspaceAgentID: agent.ID,
|
||||
WorkspaceFolder: "/another/cool/folder",
|
||||
ConfigPath: "/another/cool/folder/.devcontainer/devcontainer.json",
|
||||
},
|
||||
}
|
||||
derpMapFn = func() *tailcfg.DERPMap {
|
||||
return &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
@@ -267,6 +280,17 @@ func TestGetManifest(t *testing.T) {
|
||||
Timeout: durationpb.New(time.Duration(metadata[1].Timeout)),
|
||||
},
|
||||
}
|
||||
protoDevcontainers = []*agentproto.WorkspaceAgentDevcontainer{
|
||||
{
|
||||
Id: devcontainers[0].ID[:],
|
||||
WorkspaceFolder: devcontainers[0].WorkspaceFolder,
|
||||
},
|
||||
{
|
||||
Id: devcontainers[1].ID[:],
|
||||
WorkspaceFolder: devcontainers[1].WorkspaceFolder,
|
||||
ConfigPath: devcontainers[1].ConfigPath,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
@@ -299,6 +323,7 @@ func TestGetManifest(t *testing.T) {
|
||||
WorkspaceAgentID: agent.ID,
|
||||
Keys: nil, // all
|
||||
}).Return(metadata, nil)
|
||||
mDB.EXPECT().GetWorkspaceAgentDevcontainersByAgentID(gomock.Any(), agent.ID).Return(devcontainers, nil)
|
||||
mDB.EXPECT().GetWorkspaceByID(gomock.Any(), workspace.ID).Return(workspace, nil)
|
||||
mDB.EXPECT().GetUserByID(gomock.Any(), workspace.OwnerID).Return(owner, nil)
|
||||
|
||||
@@ -321,10 +346,11 @@ func TestGetManifest(t *testing.T) {
|
||||
// tailnet.DERPMapToProto() is extensively tested elsewhere, so it's
|
||||
// not necessary to manually recreate a big DERP map here like we
|
||||
// did for apps and metadata.
|
||||
DerpMap: tailnet.DERPMapToProto(derpMapFn()),
|
||||
Scripts: protoScripts,
|
||||
Apps: protoApps,
|
||||
Metadata: protoMetadata,
|
||||
DerpMap: tailnet.DERPMapToProto(derpMapFn()),
|
||||
Scripts: protoScripts,
|
||||
Apps: protoApps,
|
||||
Metadata: protoMetadata,
|
||||
Devcontainers: protoDevcontainers,
|
||||
}
|
||||
|
||||
// Log got and expected with spew.
|
||||
@@ -364,6 +390,7 @@ func TestGetManifest(t *testing.T) {
|
||||
WorkspaceAgentID: agent.ID,
|
||||
Keys: nil, // all
|
||||
}).Return(metadata, nil)
|
||||
mDB.EXPECT().GetWorkspaceAgentDevcontainersByAgentID(gomock.Any(), agent.ID).Return(devcontainers, nil)
|
||||
mDB.EXPECT().GetWorkspaceByID(gomock.Any(), workspace.ID).Return(workspace, nil)
|
||||
mDB.EXPECT().GetUserByID(gomock.Any(), workspace.OwnerID).Return(owner, nil)
|
||||
|
||||
@@ -386,10 +413,11 @@ func TestGetManifest(t *testing.T) {
|
||||
// tailnet.DERPMapToProto() is extensively tested elsewhere, so it's
|
||||
// not necessary to manually recreate a big DERP map here like we
|
||||
// did for apps and metadata.
|
||||
DerpMap: tailnet.DERPMapToProto(derpMapFn()),
|
||||
Scripts: protoScripts,
|
||||
Apps: protoApps,
|
||||
Metadata: protoMetadata,
|
||||
DerpMap: tailnet.DERPMapToProto(derpMapFn()),
|
||||
Scripts: protoScripts,
|
||||
Apps: protoApps,
|
||||
Metadata: protoMetadata,
|
||||
Devcontainers: protoDevcontainers,
|
||||
}
|
||||
|
||||
// Log got and expected with spew.
|
||||
|
||||
Generated
+2
@@ -14079,6 +14079,7 @@ const docTemplate = `{
|
||||
"template",
|
||||
"user",
|
||||
"workspace",
|
||||
"workspace_agent_devcontainers",
|
||||
"workspace_agent_resource_monitor",
|
||||
"workspace_dormant",
|
||||
"workspace_proxy"
|
||||
@@ -14115,6 +14116,7 @@ const docTemplate = `{
|
||||
"ResourceTemplate",
|
||||
"ResourceUser",
|
||||
"ResourceWorkspace",
|
||||
"ResourceWorkspaceAgentDevcontainers",
|
||||
"ResourceWorkspaceAgentResourceMonitor",
|
||||
"ResourceWorkspaceDormant",
|
||||
"ResourceWorkspaceProxy"
|
||||
|
||||
Generated
+2
@@ -12746,6 +12746,7 @@
|
||||
"template",
|
||||
"user",
|
||||
"workspace",
|
||||
"workspace_agent_devcontainers",
|
||||
"workspace_agent_resource_monitor",
|
||||
"workspace_dormant",
|
||||
"workspace_proxy"
|
||||
@@ -12782,6 +12783,7 @@
|
||||
"ResourceTemplate",
|
||||
"ResourceUser",
|
||||
"ResourceWorkspace",
|
||||
"ResourceWorkspaceAgentDevcontainers",
|
||||
"ResourceWorkspaceAgentResourceMonitor",
|
||||
"ResourceWorkspaceDormant",
|
||||
"ResourceWorkspaceProxy"
|
||||
|
||||
@@ -186,6 +186,7 @@ var (
|
||||
rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead},
|
||||
// Provisionerd creates workspaces resources monitor
|
||||
rbac.ResourceWorkspaceAgentResourceMonitor.Type: {policy.ActionCreate},
|
||||
rbac.ResourceWorkspaceAgentDevcontainers.Type: {policy.ActionCreate},
|
||||
}),
|
||||
Org: map[string][]rbac.Permission{},
|
||||
User: []rbac.Permission{},
|
||||
@@ -2660,6 +2661,14 @@ func (q *querier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanc
|
||||
return agent, nil
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
_, err := q.GetWorkspaceAgentByID(ctx, workspaceAgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceAgentDevcontainersByAgentID(ctx, workspaceAgentID)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceAgentLifecycleStateByIDRow, error) {
|
||||
_, err := q.GetWorkspaceAgentByID(ctx, id)
|
||||
if err != nil {
|
||||
@@ -3390,6 +3399,13 @@ func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertW
|
||||
return q.db.InsertWorkspaceAgent(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg database.InsertWorkspaceAgentDevcontainersParams) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceWorkspaceAgentDevcontainers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.InsertWorkspaceAgentDevcontainers(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) {
|
||||
// TODO: This is used by the agent, should we have an rbac check here?
|
||||
return q.db.InsertWorkspaceAgentLogSources(ctx, arg)
|
||||
|
||||
@@ -3074,6 +3074,36 @@ func (s *MethodTestSuite) TestWorkspace() {
|
||||
})
|
||||
check.Args(w.ID).Asserts(w, policy.ActionUpdate).Returns()
|
||||
}))
|
||||
s.Run("GetWorkspaceAgentDevcontainersByAgentID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
o := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
tpl := dbgen.Template(s.T(), db, database.Template{
|
||||
OrganizationID: o.ID,
|
||||
CreatedBy: u.ID,
|
||||
})
|
||||
tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
|
||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||
OrganizationID: o.ID,
|
||||
CreatedBy: u.ID,
|
||||
})
|
||||
w := dbgen.Workspace(s.T(), db, database.WorkspaceTable{
|
||||
TemplateID: tpl.ID,
|
||||
OrganizationID: o.ID,
|
||||
OwnerID: u.ID,
|
||||
})
|
||||
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
})
|
||||
b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{
|
||||
JobID: j.ID,
|
||||
WorkspaceID: w.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
})
|
||||
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: b.JobID})
|
||||
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
|
||||
d := dbgen.WorkspaceAgentDevcontainer(s.T(), db, database.WorkspaceAgentDevcontainer{WorkspaceAgentID: agt.ID})
|
||||
check.Args(agt.ID).Asserts(w, policy.ActionRead).Returns([]database.WorkspaceAgentDevcontainer{d})
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestWorkspacePortSharing() {
|
||||
@@ -5021,3 +5051,45 @@ func (s *MethodTestSuite) TestResourcesMonitor() {
|
||||
check.Args(agt.ID).Asserts(w, policy.ActionRead).Returns(monitors)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestResourcesProvisionerdserver() {
|
||||
createAgent := func(t *testing.T, db database.Store) (database.WorkspaceAgent, database.WorkspaceTable) {
|
||||
t.Helper()
|
||||
|
||||
u := dbgen.User(t, db, database.User{})
|
||||
o := dbgen.Organization(t, db, database.Organization{})
|
||||
tpl := dbgen.Template(t, db, database.Template{
|
||||
OrganizationID: o.ID,
|
||||
CreatedBy: u.ID,
|
||||
})
|
||||
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||
OrganizationID: o.ID,
|
||||
CreatedBy: u.ID,
|
||||
})
|
||||
w := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
TemplateID: tpl.ID,
|
||||
OrganizationID: o.ID,
|
||||
OwnerID: u.ID,
|
||||
})
|
||||
j := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
})
|
||||
b := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
JobID: j.ID,
|
||||
WorkspaceID: w.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
})
|
||||
res := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{JobID: b.JobID})
|
||||
agt := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ResourceID: res.ID})
|
||||
|
||||
return agt, w
|
||||
}
|
||||
|
||||
s.Run("InsertWorkspaceAgentDevcontainers", s.Subtest(func(db database.Store, check *expects) {
|
||||
agt, _ := createAgent(s.T(), db)
|
||||
check.Args(database.InsertWorkspaceAgentDevcontainersParams{
|
||||
WorkspaceAgentID: agt.ID,
|
||||
}).Asserts(rbac.ResourceWorkspaceAgentDevcontainers, policy.ActionCreate)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -255,6 +255,18 @@ func WorkspaceAgentScriptTiming(t testing.TB, db database.Store, orig database.W
|
||||
panic("failed to insert workspace agent script timing")
|
||||
}
|
||||
|
||||
func WorkspaceAgentDevcontainer(t testing.TB, db database.Store, orig database.WorkspaceAgentDevcontainer) database.WorkspaceAgentDevcontainer {
|
||||
devcontainers, err := db.InsertWorkspaceAgentDevcontainers(genCtx, database.InsertWorkspaceAgentDevcontainersParams{
|
||||
WorkspaceAgentID: takeFirst(orig.WorkspaceAgentID, uuid.New()),
|
||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||
ID: []uuid.UUID{takeFirst(orig.ID, uuid.New())},
|
||||
WorkspaceFolder: []string{takeFirst(orig.WorkspaceFolder, "/workspace")},
|
||||
ConfigPath: []string{takeFirst(orig.ConfigPath, "")},
|
||||
})
|
||||
require.NoError(t, err, "insert workspace agent devcontainer")
|
||||
return devcontainers[0]
|
||||
}
|
||||
|
||||
func Workspace(t testing.TB, db database.Store, orig database.WorkspaceTable) database.WorkspaceTable {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -237,6 +237,7 @@ type data struct {
|
||||
workspaceAgentStats []database.WorkspaceAgentStat
|
||||
workspaceAgentMemoryResourceMonitors []database.WorkspaceAgentMemoryResourceMonitor
|
||||
workspaceAgentVolumeResourceMonitors []database.WorkspaceAgentVolumeResourceMonitor
|
||||
workspaceAgentDevcontainers []database.WorkspaceAgentDevcontainer
|
||||
workspaceApps []database.WorkspaceApp
|
||||
workspaceAppAuditSessions []database.WorkspaceAppAuditSession
|
||||
workspaceAppStatsLastInsertID int64
|
||||
@@ -6696,6 +6697,22 @@ func (q *FakeQuerier) GetWorkspaceAgentByInstanceID(_ context.Context, instanceI
|
||||
return database.WorkspaceAgent{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAgentDevcontainersByAgentID(_ context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
devcontainers := make([]database.WorkspaceAgentDevcontainer, 0)
|
||||
for _, dc := range q.workspaceAgentDevcontainers {
|
||||
if dc.WorkspaceAgentID == workspaceAgentID {
|
||||
devcontainers = append(devcontainers, dc)
|
||||
}
|
||||
}
|
||||
if len(devcontainers) == 0 {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
return devcontainers, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceAgentLifecycleStateByIDRow, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -9051,6 +9068,35 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser
|
||||
return agent, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAgentDevcontainers(_ context.Context, arg database.InsertWorkspaceAgentDevcontainersParams) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, agent := range q.workspaceAgents {
|
||||
if agent.ID == arg.WorkspaceAgentID {
|
||||
var devcontainers []database.WorkspaceAgentDevcontainer
|
||||
for i, id := range arg.ID {
|
||||
devcontainers = append(devcontainers, database.WorkspaceAgentDevcontainer{
|
||||
WorkspaceAgentID: arg.WorkspaceAgentID,
|
||||
CreatedAt: arg.CreatedAt,
|
||||
ID: id,
|
||||
WorkspaceFolder: arg.WorkspaceFolder[i],
|
||||
ConfigPath: arg.ConfigPath[i],
|
||||
})
|
||||
}
|
||||
q.workspaceAgentDevcontainers = append(q.workspaceAgentDevcontainers, devcontainers...)
|
||||
return devcontainers, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errForeignKeyConstraint
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAgentLogSources(_ context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
|
||||
@@ -1515,6 +1515,13 @@ func (m queryMetricsStore) GetWorkspaceAgentByInstanceID(ctx context.Context, au
|
||||
return agent, err
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceAgentDevcontainersByAgentID(ctx, workspaceAgentID)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceAgentDevcontainersByAgentID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceAgentLifecycleStateByIDRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceAgentLifecycleStateByID(ctx, id)
|
||||
@@ -2138,6 +2145,13 @@ func (m queryMetricsStore) InsertWorkspaceAgent(ctx context.Context, arg databas
|
||||
return agent, err
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg database.InsertWorkspaceAgentDevcontainersParams) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertWorkspaceAgentDevcontainers(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertWorkspaceAgentDevcontainers").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertWorkspaceAgentLogSources(ctx, arg)
|
||||
|
||||
@@ -3172,6 +3172,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentByInstanceID(ctx, authInstance
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentByInstanceID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentByInstanceID), ctx, authInstanceID)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentDevcontainersByAgentID mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceAgentDevcontainersByAgentID", ctx, workspaceAgentID)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAgentDevcontainer)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentDevcontainersByAgentID indicates an expected call of GetWorkspaceAgentDevcontainersByAgentID.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceAgentDevcontainersByAgentID(ctx, workspaceAgentID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentDevcontainersByAgentID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentDevcontainersByAgentID), ctx, workspaceAgentID)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentLifecycleStateByID mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceAgentLifecycleStateByIDRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -4513,6 +4528,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceAgent(ctx, arg any) *gomock.Call
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgent", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgent), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentDevcontainers mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg database.InsertWorkspaceAgentDevcontainersParams) ([]database.WorkspaceAgentDevcontainer, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertWorkspaceAgentDevcontainers", ctx, arg)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAgentDevcontainer)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentDevcontainers indicates an expected call of InsertWorkspaceAgentDevcontainers.
|
||||
func (mr *MockStoreMockRecorder) InsertWorkspaceAgentDevcontainers(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentDevcontainers", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentDevcontainers), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentLogSources mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Generated
+30
@@ -1585,6 +1585,26 @@ CREATE TABLE user_status_changes (
|
||||
|
||||
COMMENT ON TABLE user_status_changes IS 'Tracks the history of user status changes';
|
||||
|
||||
CREATE TABLE workspace_agent_devcontainers (
|
||||
id uuid NOT NULL,
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
workspace_folder text NOT NULL,
|
||||
config_path text NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE workspace_agent_devcontainers IS 'Workspace agent devcontainer configuration';
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.id IS 'Unique identifier';
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.workspace_agent_id IS 'Workspace agent foreign key';
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.created_at IS 'Creation timestamp';
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.workspace_folder IS 'Workspace folder';
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.config_path IS 'Path to devcontainer.json.';
|
||||
|
||||
CREATE TABLE workspace_agent_log_sources (
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
id uuid NOT NULL,
|
||||
@@ -2250,6 +2270,9 @@ ALTER TABLE ONLY user_status_changes
|
||||
ALTER TABLE ONLY users
|
||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_devcontainers
|
||||
ADD CONSTRAINT workspace_agent_devcontainers_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_log_sources
|
||||
ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id);
|
||||
|
||||
@@ -2407,6 +2430,10 @@ CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WH
|
||||
|
||||
CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false);
|
||||
|
||||
CREATE INDEX workspace_agent_devcontainers_workspace_agent_id ON workspace_agent_devcontainers USING btree (workspace_agent_id);
|
||||
|
||||
COMMENT ON INDEX workspace_agent_devcontainers_workspace_agent_id IS 'Workspace agent foreign key and query index';
|
||||
|
||||
CREATE INDEX workspace_agent_scripts_workspace_agent_id_idx ON workspace_agent_scripts USING btree (workspace_agent_id);
|
||||
|
||||
COMMENT ON INDEX workspace_agent_scripts_workspace_agent_id_idx IS 'Foreign key support index for faster lookups';
|
||||
@@ -2680,6 +2707,9 @@ ALTER TABLE ONLY user_links
|
||||
ALTER TABLE ONLY user_status_changes
|
||||
ADD CONSTRAINT user_status_changes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_devcontainers
|
||||
ADD CONSTRAINT workspace_agent_devcontainers_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_log_sources
|
||||
ADD CONSTRAINT workspace_agent_log_sources_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ const (
|
||||
ForeignKeyUserLinksOauthRefreshTokenKeyID ForeignKeyConstraint = "user_links_oauth_refresh_token_key_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_oauth_refresh_token_key_id_fkey FOREIGN KEY (oauth_refresh_token_key_id) REFERENCES dbcrypt_keys(active_key_digest);
|
||||
ForeignKeyUserLinksUserID ForeignKeyConstraint = "user_links_user_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyUserStatusChangesUserID ForeignKeyConstraint = "user_status_changes_user_id_fkey" // ALTER TABLE ONLY user_status_changes ADD CONSTRAINT user_status_changes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
ForeignKeyWorkspaceAgentDevcontainersWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_devcontainers_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_devcontainers ADD CONSTRAINT workspace_agent_devcontainers_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceAgentLogSourcesWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_log_sources_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceAgentMemoryResourceMonitorsAgentID ForeignKeyConstraint = "workspace_agent_memory_resource_monitors_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_memory_resource_monitors ADD CONSTRAINT workspace_agent_memory_resource_monitors_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceAgentMetadataWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_metadata_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE workspace_agent_devcontainers;
|
||||
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE workspace_agent_devcontainers (
|
||||
id UUID PRIMARY KEY,
|
||||
workspace_agent_id UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
workspace_folder TEXT NOT NULL,
|
||||
config_path TEXT NOT NULL,
|
||||
FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
COMMENT ON TABLE workspace_agent_devcontainers IS 'Workspace agent devcontainer configuration';
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.id IS 'Unique identifier';
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.workspace_agent_id IS 'Workspace agent foreign key';
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.created_at IS 'Creation timestamp';
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.workspace_folder IS 'Workspace folder';
|
||||
COMMENT ON COLUMN workspace_agent_devcontainers.config_path IS 'Path to devcontainer.json.';
|
||||
|
||||
CREATE INDEX workspace_agent_devcontainers_workspace_agent_id ON workspace_agent_devcontainers (workspace_agent_id);
|
||||
|
||||
COMMENT ON INDEX workspace_agent_devcontainers_workspace_agent_id IS 'Workspace agent foreign key and query index';
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
INSERT INTO
|
||||
workspace_agent_devcontainers (
|
||||
workspace_agent_id,
|
||||
created_at,
|
||||
id,
|
||||
workspace_folder,
|
||||
config_path
|
||||
)
|
||||
VALUES (
|
||||
'45e89705-e09d-4850-bcec-f9a937f5d78d',
|
||||
'2021-09-01 00:00:00',
|
||||
'489c0a1d-387d-41f0-be55-63aa7c5d7b14',
|
||||
'/workspace',
|
||||
'/workspace/.devcontainer/devcontainer.json'
|
||||
)
|
||||
@@ -3306,6 +3306,20 @@ type WorkspaceAgent struct {
|
||||
DisplayOrder int32 `db:"display_order" json:"display_order"`
|
||||
}
|
||||
|
||||
// Workspace agent devcontainer configuration
|
||||
type WorkspaceAgentDevcontainer struct {
|
||||
// Unique identifier
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
// Workspace agent foreign key
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
// Creation timestamp
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
// Workspace folder
|
||||
WorkspaceFolder string `db:"workspace_folder" json:"workspace_folder"`
|
||||
// Path to devcontainer.json.
|
||||
ConfigPath string `db:"config_path" json:"config_path"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentLog struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
|
||||
@@ -342,6 +342,7 @@ type sqlcQuerier interface {
|
||||
GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Context, authToken uuid.UUID) (GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error)
|
||||
GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentDevcontainer, error)
|
||||
GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (GetWorkspaceAgentLifecycleStateByIDRow, error)
|
||||
GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentLogSource, error)
|
||||
GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWorkspaceAgentLogsAfterParams) ([]WorkspaceAgentLog, error)
|
||||
@@ -452,6 +453,7 @@ type sqlcQuerier interface {
|
||||
InsertVolumeResourceMonitor(ctx context.Context, arg InsertVolumeResourceMonitorParams) (WorkspaceAgentVolumeResourceMonitor, error)
|
||||
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (WorkspaceTable, error)
|
||||
InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error)
|
||||
InsertWorkspaceAgentDevcontainers(ctx context.Context, arg InsertWorkspaceAgentDevcontainersParams) ([]WorkspaceAgentDevcontainer, error)
|
||||
InsertWorkspaceAgentLogSources(ctx context.Context, arg InsertWorkspaceAgentLogSourcesParams) ([]WorkspaceAgentLogSource, error)
|
||||
InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error)
|
||||
InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error
|
||||
|
||||
@@ -12269,6 +12269,101 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceAgentDevcontainersByAgentID = `-- name: GetWorkspaceAgentDevcontainersByAgentID :many
|
||||
SELECT
|
||||
id, workspace_agent_id, created_at, workspace_folder, config_path
|
||||
FROM
|
||||
workspace_agent_devcontainers
|
||||
WHERE
|
||||
workspace_agent_id = $1
|
||||
ORDER BY
|
||||
created_at, id
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAgentDevcontainersByAgentID(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentDevcontainer, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentDevcontainersByAgentID, workspaceAgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentDevcontainer
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentDevcontainer
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.CreatedAt,
|
||||
&i.WorkspaceFolder,
|
||||
&i.ConfigPath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertWorkspaceAgentDevcontainers = `-- name: InsertWorkspaceAgentDevcontainers :many
|
||||
INSERT INTO
|
||||
workspace_agent_devcontainers (workspace_agent_id, created_at, id, workspace_folder, config_path)
|
||||
SELECT
|
||||
$1::uuid AS workspace_agent_id,
|
||||
$2::timestamptz AS created_at,
|
||||
unnest($3::uuid[]) AS id,
|
||||
unnest($4::text[]) AS workspace_folder,
|
||||
unnest($5::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
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentDevcontainersParams struct {
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ID []uuid.UUID `db:"id" json:"id"`
|
||||
WorkspaceFolder []string `db:"workspace_folder" json:"workspace_folder"`
|
||||
ConfigPath []string `db:"config_path" json:"config_path"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgentDevcontainers(ctx context.Context, arg InsertWorkspaceAgentDevcontainersParams) ([]WorkspaceAgentDevcontainer, error) {
|
||||
rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentDevcontainers,
|
||||
arg.WorkspaceAgentID,
|
||||
arg.CreatedAt,
|
||||
pq.Array(arg.ID),
|
||||
pq.Array(arg.WorkspaceFolder),
|
||||
pq.Array(arg.ConfigPath),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentDevcontainer
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentDevcontainer
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.CreatedAt,
|
||||
&i.WorkspaceFolder,
|
||||
&i.ConfigPath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const deleteWorkspaceAgentPortShare = `-- name: DeleteWorkspaceAgentPortShare :exec
|
||||
DELETE FROM
|
||||
workspace_agent_port_share
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- name: InsertWorkspaceAgentDevcontainers :many
|
||||
INSERT INTO
|
||||
workspace_agent_devcontainers (workspace_agent_id, created_at, id, workspace_folder, config_path)
|
||||
SELECT
|
||||
@workspace_agent_id::uuid AS workspace_agent_id,
|
||||
@created_at::timestamptz AS created_at,
|
||||
unnest(@id::uuid[]) AS id,
|
||||
unnest(@workspace_folder::text[]) AS workspace_folder,
|
||||
unnest(@config_path::text[]) AS config_path
|
||||
RETURNING workspace_agent_devcontainers.*;
|
||||
|
||||
-- name: GetWorkspaceAgentDevcontainersByAgentID :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
workspace_agent_devcontainers
|
||||
WHERE
|
||||
workspace_agent_id = $1
|
||||
ORDER BY
|
||||
created_at, id;
|
||||
@@ -70,6 +70,7 @@ const (
|
||||
UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type);
|
||||
UniqueUserStatusChangesPkey UniqueConstraint = "user_status_changes_pkey" // ALTER TABLE ONLY user_status_changes ADD CONSTRAINT user_status_changes_pkey PRIMARY KEY (id);
|
||||
UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
UniqueWorkspaceAgentDevcontainersPkey UniqueConstraint = "workspace_agent_devcontainers_pkey" // ALTER TABLE ONLY workspace_agent_devcontainers ADD CONSTRAINT workspace_agent_devcontainers_pkey PRIMARY KEY (id);
|
||||
UniqueWorkspaceAgentLogSourcesPkey UniqueConstraint = "workspace_agent_log_sources_pkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id);
|
||||
UniqueWorkspaceAgentMemoryResourceMonitorsPkey UniqueConstraint = "workspace_agent_memory_resource_monitors_pkey" // ALTER TABLE ONLY workspace_agent_memory_resource_monitors ADD CONSTRAINT workspace_agent_memory_resource_monitors_pkey PRIMARY KEY (agent_id);
|
||||
UniqueWorkspaceAgentMetadataPkey UniqueConstraint = "workspace_agent_metadata_pkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key);
|
||||
|
||||
@@ -2096,6 +2096,30 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
return xerrors.Errorf("insert agent scripts: %w", err)
|
||||
}
|
||||
|
||||
if devcontainers := prAgent.GetDevcontainers(); len(devcontainers) > 0 {
|
||||
var (
|
||||
devContainerIDs = make([]uuid.UUID, 0, len(devcontainers))
|
||||
devContainerWorkspaceFolders = make([]string, 0, len(devcontainers))
|
||||
devContainerConfigPaths = make([]string, 0, len(devcontainers))
|
||||
)
|
||||
for _, dc := range devcontainers {
|
||||
devContainerIDs = append(devContainerIDs, uuid.New())
|
||||
devContainerWorkspaceFolders = append(devContainerWorkspaceFolders, dc.WorkspaceFolder)
|
||||
devContainerConfigPaths = append(devContainerConfigPaths, dc.ConfigPath)
|
||||
}
|
||||
|
||||
_, err = db.InsertWorkspaceAgentDevcontainers(ctx, database.InsertWorkspaceAgentDevcontainersParams{
|
||||
WorkspaceAgentID: agentID,
|
||||
CreatedAt: dbtime.Now(),
|
||||
ID: devContainerIDs,
|
||||
WorkspaceFolder: devContainerWorkspaceFolders,
|
||||
ConfigPath: devContainerConfigPaths,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert agent devcontainer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, app := range prAgent.Apps {
|
||||
// Similar logic is duplicated in terraform/resources.go.
|
||||
slug := app.Slug
|
||||
|
||||
@@ -2190,6 +2190,37 @@ func TestInsertWorkspaceResource(t *testing.T) {
|
||||
require.Equal(t, int32(50), volMonitors[1].Threshold)
|
||||
require.Equal(t, "/volume2", volMonitors[1].Path)
|
||||
})
|
||||
|
||||
t.Run("Devcontainers", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := dbmem.New()
|
||||
job := uuid.New()
|
||||
err := insert(db, job, &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
Agents: []*sdkproto.Agent{{
|
||||
Name: "dev",
|
||||
Devcontainers: []*sdkproto.Devcontainer{
|
||||
{WorkspaceFolder: "/workspace1"},
|
||||
{WorkspaceFolder: "/workspace2", ConfigPath: "/workspace2/.devcontainer/devcontainer.json"},
|
||||
},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resources, err := db.GetWorkspaceResourcesByJobID(ctx, job)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resources, 1)
|
||||
agents, err := db.GetWorkspaceAgentsByResourceIDs(ctx, []uuid.UUID{resources[0].ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, agents, 1)
|
||||
agent := agents[0]
|
||||
devcontainers, err := db.GetWorkspaceAgentDevcontainersByAgentID(ctx, agent.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, devcontainers, 2)
|
||||
require.Equal(t, "/workspace1", devcontainers[0].WorkspaceFolder)
|
||||
require.Equal(t, "/workspace2", devcontainers[1].WorkspaceFolder)
|
||||
require.Equal(t, "/workspace2/.devcontainer/devcontainer.json", devcontainers[1].ConfigPath)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotifications(t *testing.T) {
|
||||
|
||||
@@ -294,6 +294,13 @@ var (
|
||||
Type: "workspace",
|
||||
}
|
||||
|
||||
// ResourceWorkspaceAgentDevcontainers
|
||||
// Valid Actions
|
||||
// - "ActionCreate" :: create workspace agent devcontainers
|
||||
ResourceWorkspaceAgentDevcontainers = Object{
|
||||
Type: "workspace_agent_devcontainers",
|
||||
}
|
||||
|
||||
// ResourceWorkspaceAgentResourceMonitor
|
||||
// Valid Actions
|
||||
// - "ActionCreate" :: create workspace agent resource monitor
|
||||
@@ -361,6 +368,7 @@ func AllResources() []Objecter {
|
||||
ResourceTemplate,
|
||||
ResourceUser,
|
||||
ResourceWorkspace,
|
||||
ResourceWorkspaceAgentDevcontainers,
|
||||
ResourceWorkspaceAgentResourceMonitor,
|
||||
ResourceWorkspaceDormant,
|
||||
ResourceWorkspaceProxy,
|
||||
|
||||
@@ -309,4 +309,9 @@ var RBACPermissions = map[string]PermissionDefinition{
|
||||
ActionUpdate: actDef("update workspace agent resource monitor"),
|
||||
},
|
||||
},
|
||||
"workspace_agent_devcontainers": {
|
||||
Actions: map[Action]ActionDefinition{
|
||||
ActionCreate: actDef("create workspace agent devcontainers"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -806,6 +806,21 @@ func TestRolePermissions(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "WorkspaceAgentDevcontainers",
|
||||
Actions: []policy.Action{policy.ActionCreate},
|
||||
Resource: rbac.ResourceWorkspaceAgentDevcontainers,
|
||||
AuthorizeMap: map[bool][]hasAuthSubjects{
|
||||
true: {owner},
|
||||
false: {
|
||||
memberMe, orgMemberMe, otherOrgMember,
|
||||
orgAdmin, otherOrgAdmin,
|
||||
orgAuditor, otherOrgAuditor,
|
||||
templateAdmin, orgTemplateAdmin, otherOrgTemplateAdmin,
|
||||
userAdmin, orgUserAdmin, otherOrgUserAdmin,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// We expect every permission to be tested above.
|
||||
|
||||
Reference in New Issue
Block a user