mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: track workspace resource monitors in telemetry (#16776)
Addresses https://github.com/coder/nexus/issues/195. Specifically, just the "tracking templates" requirement: > ## Tracking in templates > To enable resource alerts, a user must add the resource_monitoring block to a template's coder_agent resource. We'd like to track if customers have any resource monitoring enabled on a per-deployment basis. Even better, we could identify which templates are using resource monitoring.
This commit is contained in:
@@ -1438,6 +1438,17 @@ 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) {
|
||||
// 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) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceNotificationMessage); err != nil {
|
||||
return database.FetchNewMessageMetadataRow{}, err
|
||||
@@ -1459,6 +1470,17 @@ 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) {
|
||||
// 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) {
|
||||
return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -2503,6 +2503,19 @@ func (q *FakeQuerier) FetchMemoryResourceMonitorsByAgentID(_ context.Context, ag
|
||||
return database.WorkspaceAgentMemoryResourceMonitor{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
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) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
@@ -2547,6 +2560,19 @@ func (q *FakeQuerier) FetchVolumesResourceMonitorsByAgentID(_ context.Context, a
|
||||
return monitors, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -12398,6 +12398,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
|
||||
@@ -12439,6 +12479,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 (
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user