From 7207ede51ce908eb09418f4307d57969bef90436 Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Mon, 3 Mar 2025 13:55:20 +0000 Subject: [PATCH 1/4] sql queries --- .../queries/workspaceagentresourcemonitors.sql | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/coderd/database/queries/workspaceagentresourcemonitors.sql b/coderd/database/queries/workspaceagentresourcemonitors.sql index 84ee5c67b37ef..50e7e818f7c67 100644 --- a/coderd/database/queries/workspaceagentresourcemonitors.sql +++ b/coderd/database/queries/workspaceagentresourcemonitors.sql @@ -1,3 +1,19 @@ +-- name: FetchVolumesResourceMonitorsUpdatedAfter :many +SELECT + * +FROM + workspace_agent_volume_resource_monitors +WHERE + updated_at > $1; + +-- name: FetchMemoryResourceMonitorsUpdatedAfter :many +SELECT + * +FROM + workspace_agent_memory_resource_monitors +WHERE + updated_at > $1; + -- name: FetchMemoryResourceMonitorsByAgentID :one SELECT * From 45f465da10e5b2989c4dd0fa008b4e67a9ef3b51 Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Mon, 3 Mar 2025 13:59:02 +0000 Subject: [PATCH 2/4] make gen --- coderd/database/dbauthz/dbauthz.go | 8 +++ coderd/database/dbmem/dbmem.go | 8 +++ coderd/database/dbmetrics/querymetrics.go | 14 ++++ coderd/database/dbmock/dbmock.go | 30 +++++++++ coderd/database/querier.go | 2 + coderd/database/queries.sql.go | 81 +++++++++++++++++++++++ 6 files changed, 143 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index a39ba8d4172f0..d4c5ea19bbc18 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1438,6 +1438,10 @@ func (q *querier) FetchMemoryResourceMonitorsByAgentID(ctx context.Context, agen return q.db.FetchMemoryResourceMonitorsByAgentID(ctx, agentID) } +func (q *querier) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { + panic("not implemented") +} + func (q *querier) FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceNotificationMessage); err != nil { return database.FetchNewMessageMetadataRow{}, err @@ -1459,6 +1463,10 @@ func (q *querier) FetchVolumesResourceMonitorsByAgentID(ctx context.Context, age return q.db.FetchVolumesResourceMonitorsByAgentID(ctx, agentID) } +func (q *querier) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { + panic("not implemented") +} + func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id) } diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index cc559a7e77f16..ff797ab6f9b1a 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2391,6 +2391,10 @@ func (q *FakeQuerier) FetchMemoryResourceMonitorsByAgentID(_ context.Context, ag return database.WorkspaceAgentMemoryResourceMonitor{}, sql.ErrNoRows } +func (q *FakeQuerier) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { + panic("not implemented") +} + func (q *FakeQuerier) FetchNewMessageMetadata(_ context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { err := validateDatabaseType(arg) if err != nil { @@ -2435,6 +2439,10 @@ func (q *FakeQuerier) FetchVolumesResourceMonitorsByAgentID(_ context.Context, a return monitors, nil } +func (q *FakeQuerier) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { + panic("not implemented") +} + func (q *FakeQuerier) GetAPIKeyByID(_ context.Context, id string) (database.APIKey, error) { q.mutex.RLock() defer q.mutex.RUnlock() diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index d05ec5f5acdf9..ab6fbe5174e7f 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -451,6 +451,13 @@ func (m queryMetricsStore) FetchMemoryResourceMonitorsByAgentID(ctx context.Cont return r0, r1 } +func (m queryMetricsStore) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { + start := time.Now() + r0, r1 := m.s.FetchMemoryResourceMonitorsUpdatedAfter(ctx, updatedAt) + m.queryLatencies.WithLabelValues("FetchMemoryResourceMonitorsUpdatedAfter").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { start := time.Now() r0, r1 := m.s.FetchNewMessageMetadata(ctx, arg) @@ -465,6 +472,13 @@ func (m queryMetricsStore) FetchVolumesResourceMonitorsByAgentID(ctx context.Con return r0, r1 } +func (m queryMetricsStore) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { + start := time.Now() + r0, r1 := m.s.FetchVolumesResourceMonitorsUpdatedAfter(ctx, updatedAt) + m.queryLatencies.WithLabelValues("FetchVolumesResourceMonitorsUpdatedAfter").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { start := time.Now() apiKey, err := m.s.GetAPIKeyByID(ctx, id) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index 39f148d90e20e..46e4dbbf4ea2a 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -787,6 +787,21 @@ func (mr *MockStoreMockRecorder) FetchMemoryResourceMonitorsByAgentID(ctx, agent return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMemoryResourceMonitorsByAgentID", reflect.TypeOf((*MockStore)(nil).FetchMemoryResourceMonitorsByAgentID), ctx, agentID) } +// FetchMemoryResourceMonitorsUpdatedAfter mocks base method. +func (m *MockStore) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchMemoryResourceMonitorsUpdatedAfter", ctx, updatedAt) + ret0, _ := ret[0].([]database.WorkspaceAgentMemoryResourceMonitor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchMemoryResourceMonitorsUpdatedAfter indicates an expected call of FetchMemoryResourceMonitorsUpdatedAfter. +func (mr *MockStoreMockRecorder) FetchMemoryResourceMonitorsUpdatedAfter(ctx, updatedAt any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchMemoryResourceMonitorsUpdatedAfter", reflect.TypeOf((*MockStore)(nil).FetchMemoryResourceMonitorsUpdatedAfter), ctx, updatedAt) +} + // FetchNewMessageMetadata mocks base method. func (m *MockStore) FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { m.ctrl.T.Helper() @@ -817,6 +832,21 @@ func (mr *MockStoreMockRecorder) FetchVolumesResourceMonitorsByAgentID(ctx, agen return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchVolumesResourceMonitorsByAgentID", reflect.TypeOf((*MockStore)(nil).FetchVolumesResourceMonitorsByAgentID), ctx, agentID) } +// FetchVolumesResourceMonitorsUpdatedAfter mocks base method. +func (m *MockStore) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchVolumesResourceMonitorsUpdatedAfter", ctx, updatedAt) + ret0, _ := ret[0].([]database.WorkspaceAgentVolumeResourceMonitor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchVolumesResourceMonitorsUpdatedAfter indicates an expected call of FetchVolumesResourceMonitorsUpdatedAfter. +func (mr *MockStoreMockRecorder) FetchVolumesResourceMonitorsUpdatedAfter(ctx, updatedAt any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchVolumesResourceMonitorsUpdatedAfter", reflect.TypeOf((*MockStore)(nil).FetchVolumesResourceMonitorsUpdatedAfter), ctx, updatedAt) +} + // GetAPIKeyByID mocks base method. func (m *MockStore) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { m.ctrl.T.Helper() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 6bae27ec1f3d4..4fe20f3fcd806 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -113,9 +113,11 @@ type sqlcQuerier interface { EnqueueNotificationMessage(ctx context.Context, arg EnqueueNotificationMessageParams) error FavoriteWorkspace(ctx context.Context, id uuid.UUID) error FetchMemoryResourceMonitorsByAgentID(ctx context.Context, agentID uuid.UUID) (WorkspaceAgentMemoryResourceMonitor, error) + FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]WorkspaceAgentMemoryResourceMonitor, error) // This is used to build up the notification_message's JSON payload. FetchNewMessageMetadata(ctx context.Context, arg FetchNewMessageMetadataParams) (FetchNewMessageMetadataRow, error) FetchVolumesResourceMonitorsByAgentID(ctx context.Context, agentID uuid.UUID) ([]WorkspaceAgentVolumeResourceMonitor, error) + FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]WorkspaceAgentVolumeResourceMonitor, error) GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) // there is no unique constraint on empty token names GetAPIKeyByName(ctx context.Context, arg GetAPIKeyByNameParams) (APIKey, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 0891bc8c9fcc6..1bc0261c1755a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -12372,6 +12372,46 @@ func (q *sqlQuerier) FetchMemoryResourceMonitorsByAgentID(ctx context.Context, a return i, err } +const fetchMemoryResourceMonitorsUpdatedAfter = `-- name: FetchMemoryResourceMonitorsUpdatedAfter :many +SELECT + agent_id, enabled, threshold, created_at, updated_at, state, debounced_until +FROM + workspace_agent_memory_resource_monitors +WHERE + updated_at > $1 +` + +func (q *sqlQuerier) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]WorkspaceAgentMemoryResourceMonitor, error) { + rows, err := q.db.QueryContext(ctx, fetchMemoryResourceMonitorsUpdatedAfter, updatedAt) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentMemoryResourceMonitor + for rows.Next() { + var i WorkspaceAgentMemoryResourceMonitor + if err := rows.Scan( + &i.AgentID, + &i.Enabled, + &i.Threshold, + &i.CreatedAt, + &i.UpdatedAt, + &i.State, + &i.DebouncedUntil, + ); 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 fetchVolumesResourceMonitorsByAgentID = `-- name: FetchVolumesResourceMonitorsByAgentID :many SELECT agent_id, enabled, threshold, path, created_at, updated_at, state, debounced_until @@ -12413,6 +12453,47 @@ func (q *sqlQuerier) FetchVolumesResourceMonitorsByAgentID(ctx context.Context, return items, nil } +const fetchVolumesResourceMonitorsUpdatedAfter = `-- name: FetchVolumesResourceMonitorsUpdatedAfter :many +SELECT + agent_id, enabled, threshold, path, created_at, updated_at, state, debounced_until +FROM + workspace_agent_volume_resource_monitors +WHERE + updated_at > $1 +` + +func (q *sqlQuerier) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]WorkspaceAgentVolumeResourceMonitor, error) { + rows, err := q.db.QueryContext(ctx, fetchVolumesResourceMonitorsUpdatedAfter, updatedAt) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentVolumeResourceMonitor + for rows.Next() { + var i WorkspaceAgentVolumeResourceMonitor + if err := rows.Scan( + &i.AgentID, + &i.Enabled, + &i.Threshold, + &i.Path, + &i.CreatedAt, + &i.UpdatedAt, + &i.State, + &i.DebouncedUntil, + ); 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 insertMemoryResourceMonitor = `-- name: InsertMemoryResourceMonitor :one INSERT INTO workspace_agent_memory_resource_monitors ( From ea338fda3312f812f044c5efc8ff3cf877a8ac23 Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Mon, 3 Mar 2025 13:59:10 +0000 Subject: [PATCH 3/4] make gen impl --- coderd/database/dbauthz/dbauthz.go | 18 +++++++++++++++-- coderd/database/dbauthz/dbauthz_test.go | 8 ++++++++ coderd/database/dbmem/dbmem.go | 26 +++++++++++++++++++++---- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index d4c5ea19bbc18..ba19184dc1a94 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1439,7 +1439,14 @@ func (q *querier) FetchMemoryResourceMonitorsByAgentID(ctx context.Context, agen } func (q *querier) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { - panic("not implemented") + // Ideally, we would return a list of monitors that the user has access to. However, that check would need to + // be implemented similarly to GetWorkspaces, which is more complex than what we're doing here. Since this query + // was introduced for telemetry, we perform a simpler check. + if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceWorkspaceAgentResourceMonitor); err != nil { + return nil, err + } + + return q.db.FetchMemoryResourceMonitorsUpdatedAfter(ctx, updatedAt) } func (q *querier) FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { @@ -1464,7 +1471,14 @@ func (q *querier) FetchVolumesResourceMonitorsByAgentID(ctx context.Context, age } func (q *querier) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { - panic("not implemented") + // Ideally, we would return a list of monitors that the user has access to. However, that check would need to + // be implemented similarly to GetWorkspaces, which is more complex than what we're doing here. Since this query + // was introduced for telemetry, we perform a simpler check. + if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceWorkspaceAgentResourceMonitor); err != nil { + return nil, err + } + + return q.db.FetchVolumesResourceMonitorsUpdatedAfter(ctx, updatedAt) } func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 12d6d8804e3e4..a2ac739042366 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -4919,6 +4919,14 @@ func (s *MethodTestSuite) TestResourcesMonitor() { }).Asserts(rbac.ResourceWorkspaceAgentResourceMonitor, policy.ActionUpdate) })) + s.Run("FetchMemoryResourceMonitorsUpdatedAfter", s.Subtest(func(db database.Store, check *expects) { + check.Args(dbtime.Now()).Asserts(rbac.ResourceWorkspaceAgentResourceMonitor, policy.ActionRead) + })) + + s.Run("FetchVolumesResourceMonitorsUpdatedAfter", s.Subtest(func(db database.Store, check *expects) { + check.Args(dbtime.Now()).Asserts(rbac.ResourceWorkspaceAgentResourceMonitor, policy.ActionRead) + })) + s.Run("FetchMemoryResourceMonitorsByAgentID", s.Subtest(func(db database.Store, check *expects) { agt, w := createAgent(s.T(), db) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index ff797ab6f9b1a..b750b6a22a572 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2391,8 +2391,17 @@ func (q *FakeQuerier) FetchMemoryResourceMonitorsByAgentID(_ context.Context, ag return database.WorkspaceAgentMemoryResourceMonitor{}, sql.ErrNoRows } -func (q *FakeQuerier) FetchMemoryResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { - panic("not implemented") +func (q *FakeQuerier) FetchMemoryResourceMonitorsUpdatedAfter(_ context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + monitors := []database.WorkspaceAgentMemoryResourceMonitor{} + for _, monitor := range q.workspaceAgentMemoryResourceMonitors { + if monitor.UpdatedAt.After(updatedAt) { + monitors = append(monitors, monitor) + } + } + return monitors, nil } func (q *FakeQuerier) FetchNewMessageMetadata(_ context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) { @@ -2439,8 +2448,17 @@ func (q *FakeQuerier) FetchVolumesResourceMonitorsByAgentID(_ context.Context, a return monitors, nil } -func (q *FakeQuerier) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { - panic("not implemented") +func (q *FakeQuerier) FetchVolumesResourceMonitorsUpdatedAfter(_ context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + monitors := []database.WorkspaceAgentVolumeResourceMonitor{} + for _, monitor := range q.workspaceAgentVolumeResourceMonitors { + if monitor.UpdatedAt.After(updatedAt) { + monitors = append(monitors, monitor) + } + } + return monitors, nil } func (q *FakeQuerier) GetAPIKeyByID(_ context.Context, id string) (database.APIKey, error) { From b92060a7f31d2eb7ae72d8639e7bce4d70a57286 Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Mon, 3 Mar 2025 14:19:53 +0000 Subject: [PATCH 4/4] telemetry --- coderd/telemetry/telemetry.go | 104 +++++++++++++++++++++++------ coderd/telemetry/telemetry_test.go | 4 ++ 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index e3d50da29e5cb..8956fed23990e 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -624,6 +624,28 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) { } return nil }) + eg.Go(func() error { + memoryMonitors, err := r.options.Database.FetchMemoryResourceMonitorsUpdatedAfter(ctx, createdAfter) + if err != nil { + return xerrors.Errorf("get memory resource monitors: %w", err) + } + snapshot.WorkspaceAgentMemoryResourceMonitors = make([]WorkspaceAgentMemoryResourceMonitor, 0, len(memoryMonitors)) + for _, monitor := range memoryMonitors { + snapshot.WorkspaceAgentMemoryResourceMonitors = append(snapshot.WorkspaceAgentMemoryResourceMonitors, ConvertWorkspaceAgentMemoryResourceMonitor(monitor)) + } + return nil + }) + eg.Go(func() error { + volumeMonitors, err := r.options.Database.FetchVolumesResourceMonitorsUpdatedAfter(ctx, createdAfter) + if err != nil { + return xerrors.Errorf("get volume resource monitors: %w", err) + } + snapshot.WorkspaceAgentVolumeResourceMonitors = make([]WorkspaceAgentVolumeResourceMonitor, 0, len(volumeMonitors)) + for _, monitor := range volumeMonitors { + snapshot.WorkspaceAgentVolumeResourceMonitors = append(snapshot.WorkspaceAgentVolumeResourceMonitors, ConvertWorkspaceAgentVolumeResourceMonitor(monitor)) + } + return nil + }) eg.Go(func() error { proxies, err := r.options.Database.GetWorkspaceProxies(ctx) if err != nil { @@ -765,6 +787,26 @@ func ConvertWorkspaceAgent(agent database.WorkspaceAgent) WorkspaceAgent { return snapAgent } +func ConvertWorkspaceAgentMemoryResourceMonitor(monitor database.WorkspaceAgentMemoryResourceMonitor) WorkspaceAgentMemoryResourceMonitor { + return WorkspaceAgentMemoryResourceMonitor{ + AgentID: monitor.AgentID, + Enabled: monitor.Enabled, + Threshold: monitor.Threshold, + CreatedAt: monitor.CreatedAt, + UpdatedAt: monitor.UpdatedAt, + } +} + +func ConvertWorkspaceAgentVolumeResourceMonitor(monitor database.WorkspaceAgentVolumeResourceMonitor) WorkspaceAgentVolumeResourceMonitor { + return WorkspaceAgentVolumeResourceMonitor{ + AgentID: monitor.AgentID, + Enabled: monitor.Enabled, + Threshold: monitor.Threshold, + CreatedAt: monitor.CreatedAt, + UpdatedAt: monitor.UpdatedAt, + } +} + // ConvertWorkspaceAgentStat anonymizes a workspace agent stat. func ConvertWorkspaceAgentStat(stat database.GetWorkspaceAgentStatsRow) WorkspaceAgentStat { return WorkspaceAgentStat{ @@ -1083,28 +1125,30 @@ func ConvertTelemetryItem(item database.TelemetryItem) TelemetryItem { type Snapshot struct { DeploymentID string `json:"deployment_id"` - APIKeys []APIKey `json:"api_keys"` - CLIInvocations []clitelemetry.Invocation `json:"cli_invocations"` - ExternalProvisioners []ExternalProvisioner `json:"external_provisioners"` - Licenses []License `json:"licenses"` - ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"` - TemplateVersions []TemplateVersion `json:"template_versions"` - Templates []Template `json:"templates"` - Users []User `json:"users"` - Groups []Group `json:"groups"` - GroupMembers []GroupMember `json:"group_members"` - WorkspaceAgentStats []WorkspaceAgentStat `json:"workspace_agent_stats"` - WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"` - WorkspaceApps []WorkspaceApp `json:"workspace_apps"` - WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"` - WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"` - WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"` - WorkspaceResources []WorkspaceResource `json:"workspace_resources"` - WorkspaceModules []WorkspaceModule `json:"workspace_modules"` - Workspaces []Workspace `json:"workspaces"` - NetworkEvents []NetworkEvent `json:"network_events"` - Organizations []Organization `json:"organizations"` - TelemetryItems []TelemetryItem `json:"telemetry_items"` + APIKeys []APIKey `json:"api_keys"` + CLIInvocations []clitelemetry.Invocation `json:"cli_invocations"` + ExternalProvisioners []ExternalProvisioner `json:"external_provisioners"` + Licenses []License `json:"licenses"` + ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"` + TemplateVersions []TemplateVersion `json:"template_versions"` + Templates []Template `json:"templates"` + Users []User `json:"users"` + Groups []Group `json:"groups"` + GroupMembers []GroupMember `json:"group_members"` + WorkspaceAgentStats []WorkspaceAgentStat `json:"workspace_agent_stats"` + WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"` + WorkspaceApps []WorkspaceApp `json:"workspace_apps"` + WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"` + WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"` + WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"` + WorkspaceResources []WorkspaceResource `json:"workspace_resources"` + WorkspaceAgentMemoryResourceMonitors []WorkspaceAgentMemoryResourceMonitor `json:"workspace_agent_memory_resource_monitors"` + WorkspaceAgentVolumeResourceMonitors []WorkspaceAgentVolumeResourceMonitor `json:"workspace_agent_volume_resource_monitors"` + WorkspaceModules []WorkspaceModule `json:"workspace_modules"` + Workspaces []Workspace `json:"workspaces"` + NetworkEvents []NetworkEvent `json:"network_events"` + Organizations []Organization `json:"organizations"` + TelemetryItems []TelemetryItem `json:"telemetry_items"` } // Deployment contains information about the host running Coder. @@ -1232,6 +1276,22 @@ type WorkspaceAgentStat struct { SessionCountSSH int64 `json:"session_count_ssh"` } +type WorkspaceAgentMemoryResourceMonitor struct { + AgentID uuid.UUID `json:"agent_id"` + Enabled bool `json:"enabled"` + Threshold int32 `json:"threshold"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type WorkspaceAgentVolumeResourceMonitor struct { + AgentID uuid.UUID `json:"agent_id"` + Enabled bool `json:"enabled"` + Threshold int32 `json:"threshold"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + type WorkspaceApp struct { ID uuid.UUID `json:"id"` CreatedAt time.Time `json:"created_at"` diff --git a/coderd/telemetry/telemetry_test.go b/coderd/telemetry/telemetry_test.go index 29fcb644fc88f..6f97ce8a1270b 100644 --- a/coderd/telemetry/telemetry_test.go +++ b/coderd/telemetry/telemetry_test.go @@ -112,6 +112,8 @@ func TestTelemetry(t *testing.T) { _, _ = dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{}) _ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{}) + _ = dbgen.WorkspaceAgentMemoryResourceMonitor(t, db, database.WorkspaceAgentMemoryResourceMonitor{}) + _ = dbgen.WorkspaceAgentVolumeResourceMonitor(t, db, database.WorkspaceAgentVolumeResourceMonitor{}) _, snapshot := collectSnapshot(t, db, nil) require.Len(t, snapshot.ProvisionerJobs, 1) @@ -133,6 +135,8 @@ func TestTelemetry(t *testing.T) { require.Len(t, snapshot.Organizations, 1) // We create one item manually above. The other is TelemetryEnabled, created by the snapshotter. require.Len(t, snapshot.TelemetryItems, 2) + require.Len(t, snapshot.WorkspaceAgentMemoryResourceMonitors, 1) + require.Len(t, snapshot.WorkspaceAgentVolumeResourceMonitors, 1) wsa := snapshot.WorkspaceAgents[0] require.Len(t, wsa.Subsystems, 2) require.Equal(t, string(database.WorkspaceAgentSubsystemEnvbox), wsa.Subsystems[0])