Skip to content

Commit d0156b3

Browse files
committed
Add Post metadata endpoint to API
1 parent 465e0d8 commit d0156b3

24 files changed

+694
-53
lines changed

agent/agent.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func (a *agent) runLoop(ctx context.Context) {
207207
}
208208
}
209209

210-
func collectMetadata(ctx context.Context, md agentsdk.MetadataDescription) agentsdk.MetadataResult {
210+
func collectMetadata(ctx context.Context, md agentsdk.MetadataDescription) codersdk.WorkspaceAgentMetadataResult {
211211
timeout := md.Timeout
212212
if timeout == 0 {
213213
timeout = md.Interval
@@ -236,7 +236,7 @@ func collectMetadata(ctx context.Context, md agentsdk.MetadataDescription) agent
236236
out.Truncate(bufLimit)
237237
}
238238

239-
result := agentsdk.MetadataResult{
239+
result := codersdk.WorkspaceAgentMetadataResult{
240240
CollectedAt: collectedAt,
241241
Key: md.Key,
242242
Value: out.String(),
@@ -250,6 +250,8 @@ func collectMetadata(ctx context.Context, md agentsdk.MetadataDescription) agent
250250
func (a *agent) reportMetadataLoop(ctx context.Context) {
251251
// In production, the minimum report interval is one second because
252252
// `coder_agent.metadata` accepts `interval` in integer seconds.
253+
// In tests, it helps to set shorter intervals because engineers are
254+
// expensive.
253255
baseInterval := time.Second
254256
if flag.Lookup("test.v") != nil {
255257
baseInterval = time.Millisecond * 100
@@ -258,7 +260,7 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
258260
var (
259261
baseTicker = time.NewTicker(baseInterval)
260262
lastCollectedAts = make(map[string]time.Time)
261-
metadataResults = make(chan agentsdk.MetadataResult, 16)
263+
metadataResults = make(chan codersdk.WorkspaceAgentMetadataResult, 16)
262264
)
263265
defer baseTicker.Stop()
264266

@@ -273,6 +275,19 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
273275
a.logger.Error(ctx, "report metadata", slog.Error(err))
274276
}
275277
case <-baseTicker.C:
278+
if len(metadataResults) > cap(metadataResults)/2 {
279+
// If we're backpressured on sending back results, we risk
280+
// runaway goroutine growth and/or overloading coderd. So,
281+
// we just skip the collection. Since we never update
282+
// "lastCollectedAt" for this key, we'll retry the collection
283+
// on the next tick.
284+
a.logger.Debug(
285+
ctx, "metadata collection backpressured",
286+
slog.F("queue_len", len(metadataResults)),
287+
)
288+
continue
289+
}
290+
276291
manifest := a.manifest.Load()
277292
if manifest == nil {
278293
continue
@@ -302,14 +317,7 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
302317
continue
303318
}
304319
}
305-
if len(metadataResults) > cap(metadataResults)/2 {
306-
// If we're backpressured on sending back results, we risk
307-
// runaway goroutine growth and/or overloading coderd. So,
308-
// we just skip the collection. Since we never update
309-
// "lastCollectedAt" for this key, we'll retry the collection
310-
// on the next tick.
311-
continue
312-
}
320+
313321
go func(md agentsdk.MetadataDescription) {
314322
select {
315323
case <-ctx.Done():

coderd/apidoc/docs.go

Lines changed: 76 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 72 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ func New(options *Options) *API {
598598
r.Get("/coordinate", api.workspaceAgentCoordinate)
599599
r.Post("/report-stats", api.workspaceAgentReportStats)
600600
r.Post("/report-lifecycle", api.workspaceAgentReportLifecycle)
601+
r.Post("/metadata", api.workspaceAgentPostMetadata)
601602
})
602603
r.Route("/{workspaceagent}", func(r chi.Router) {
603604
r.Use(

coderd/database/dbauthz/querier.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,20 @@ func (q *querier) InsertWorkspaceAgentStat(ctx context.Context, arg database.Ins
14681468
return q.db.InsertWorkspaceAgentStat(ctx, arg)
14691469
}
14701470

1471+
func (q *querier) InsertOrUpdateWorkspaceAgentMetadata(ctx context.Context, arg database.InsertOrUpdateWorkspaceAgentMetadataParams) error {
1472+
workspace, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceAgentID)
1473+
if err != nil {
1474+
return err
1475+
}
1476+
1477+
err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace)
1478+
if err != nil {
1479+
return err
1480+
}
1481+
1482+
return q.db.InsertOrUpdateWorkspaceAgentMetadata(ctx, arg)
1483+
}
1484+
14711485
func (q *querier) UpdateWorkspaceAppHealthByID(ctx context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
14721486
// TODO: This is a workspace agent operation. Should users be able to query this?
14731487
workspace, err := q.db.GetWorkspaceByWorkspaceAppID(ctx, arg.ID)

coderd/database/dbfake/databasefake.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ type data struct {
123123
templateVersionVariables []database.TemplateVersionVariable
124124
templates []database.Template
125125
workspaceAgents []database.WorkspaceAgent
126+
workspaceAgentMetadata []database.WorkspaceAgentMetadatum
126127
workspaceApps []database.WorkspaceApp
127128
workspaceBuilds []database.WorkspaceBuild
128129
workspaceBuildParameters []database.WorkspaceBuildParameter
@@ -2666,6 +2667,30 @@ func (q *fakeQuerier) InsertAPIKey(_ context.Context, arg database.InsertAPIKeyP
26662667
return key, nil
26672668
}
26682669

2670+
func (q *fakeQuerier) InsertOrUpdateWorkspaceAgentMetadata(_ context.Context, arg database.InsertOrUpdateWorkspaceAgentMetadataParams) error {
2671+
q.mutex.Lock()
2672+
defer q.mutex.Unlock()
2673+
2674+
updated := database.WorkspaceAgentMetadatum{
2675+
WorkspaceID: arg.WorkspaceID,
2676+
WorkspaceAgentID: arg.WorkspaceAgentID,
2677+
Key: arg.Key,
2678+
Value: arg.Value,
2679+
Error: arg.Error,
2680+
CollectedAt: arg.CollectedAt,
2681+
}
2682+
2683+
for i, m := range q.workspaceAgentMetadata {
2684+
if m.WorkspaceAgentID == arg.WorkspaceAgentID {
2685+
q.workspaceAgentMetadata[i] = updated
2686+
return nil
2687+
}
2688+
}
2689+
2690+
q.workspaceAgentMetadata = append(q.workspaceAgentMetadata, updated)
2691+
return nil
2692+
}
2693+
26692694
func (q *fakeQuerier) InsertFile(_ context.Context, arg database.InsertFileParams) (database.File, error) {
26702695
if err := validateDatabaseType(arg); err != nil {
26712696
return database.File{}, err

coderd/database/dump.sql

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP TABLE workspace_agent_metadata;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE workspace_agent_metadata (
2+
workspace_id uuid NOT NULL,
3+
workspace_agent_id uuid NOT NULL,
4+
key character varying(128) NOT NULL,
5+
value text NOT NULL,
6+
error text NOT NULL,
7+
collected_at timestamp with time zone NOT NULL,
8+
PRIMARY KEY (workspace_agent_id, key),
9+
FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE,
10+
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
11+
);

0 commit comments

Comments
 (0)