-
Notifications
You must be signed in to change notification settings - Fork 902
feat: add prometheus metric for tracking user statuses #15281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
14eb20f
9b0d26d
bf9e534
3cf9980
1b4f502
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ import ( | |
"github.com/coder/coder/v2/tailnet" | ||
"github.com/coder/coder/v2/tailnet/tailnettest" | ||
"github.com/coder/coder/v2/testutil" | ||
"github.com/coder/quartz" | ||
) | ||
|
||
func TestActiveUsers(t *testing.T) { | ||
|
@@ -98,7 +99,7 @@ func TestActiveUsers(t *testing.T) { | |
t.Run(tc.Name, func(t *testing.T) { | ||
t.Parallel() | ||
registry := prometheus.NewRegistry() | ||
closeFunc, err := prometheusmetrics.ActiveUsers(context.Background(), registry, tc.Database(t), time.Millisecond) | ||
closeFunc, err := prometheusmetrics.ActiveUsers(context.Background(), slogtest.Make(t, nil), registry, tc.Database(t), time.Millisecond) | ||
require.NoError(t, err) | ||
t.Cleanup(closeFunc) | ||
|
||
|
@@ -112,6 +113,100 @@ func TestActiveUsers(t *testing.T) { | |
} | ||
} | ||
|
||
func TestUsers(t *testing.T) { | ||
t.Parallel() | ||
|
||
for _, tc := range []struct { | ||
Name string | ||
Database func(t *testing.T) database.Store | ||
Count map[database.UserStatus]int | ||
}{{ | ||
Name: "None", | ||
Database: func(t *testing.T) database.Store { | ||
return dbmem.New() | ||
}, | ||
Count: map[database.UserStatus]int{}, | ||
}, { | ||
Name: "One", | ||
Database: func(t *testing.T) database.Store { | ||
db := dbmem.New() | ||
dbgen.User(t, db, database.User{Status: database.UserStatusActive}) | ||
return db | ||
}, | ||
Count: map[database.UserStatus]int{database.UserStatusActive: 1}, | ||
}, { | ||
Name: "MultipleStatuses", | ||
Database: func(t *testing.T) database.Store { | ||
db := dbmem.New() | ||
|
||
dbgen.User(t, db, database.User{Status: database.UserStatusActive}) | ||
dbgen.User(t, db, database.User{Status: database.UserStatusDormant}) | ||
|
||
return db | ||
}, | ||
Count: map[database.UserStatus]int{database.UserStatusActive: 1, database.UserStatusDormant: 1}, | ||
}, { | ||
Name: "MultipleActive", | ||
Database: func(t *testing.T) database.Store { | ||
db := dbmem.New() | ||
dbgen.User(t, db, database.User{Status: database.UserStatusActive}) | ||
dbgen.User(t, db, database.User{Status: database.UserStatusActive}) | ||
dbgen.User(t, db, database.User{Status: database.UserStatusActive}) | ||
return db | ||
}, | ||
Count: map[database.UserStatus]int{database.UserStatusActive: 3}, | ||
}} { | ||
tc := tc | ||
t.Run(tc.Name, func(t *testing.T) { | ||
t.Parallel() | ||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) | ||
defer cancel() | ||
|
||
registry := prometheus.NewRegistry() | ||
mClock := quartz.NewMock(t) | ||
db := tc.Database(t) | ||
closeFunc, err := prometheusmetrics.Users(context.Background(), slogtest.Make(t, nil), mClock, registry, db, time.Millisecond) | ||
require.NoError(t, err) | ||
t.Cleanup(closeFunc) | ||
|
||
_, w := mClock.AdvanceNext() | ||
w.MustWait(ctx) | ||
|
||
checkFn := func() bool { | ||
metrics, err := registry.Gather() | ||
if err != nil { | ||
return false | ||
} | ||
|
||
// If we get no metrics and we know none should exist, bail | ||
// early. If we get no metrics but we expect some, retry. | ||
if len(metrics) == 0 { | ||
return len(tc.Count) == 0 | ||
} | ||
|
||
for _, metric := range metrics[0].Metric { | ||
if tc.Count[database.UserStatus(*metric.Label[0].Value)] != int(metric.Gauge.GetValue()) { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
require.Eventually(t, checkFn, testutil.WaitShort, testutil.IntervalFast) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hhmm, do we need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was my assumption that |
||
|
||
// Add another dormant user and ensure it updates | ||
dbgen.User(t, db, database.User{Status: database.UserStatusDormant}) | ||
tc.Count[database.UserStatusDormant]++ | ||
|
||
_, w = mClock.AdvanceNext() | ||
w.MustWait(ctx) | ||
|
||
require.Eventually(t, checkFn, testutil.WaitShort, testutil.IntervalFast) | ||
}) | ||
} | ||
} | ||
|
||
func TestWorkspaceLatestBuildTotals(t *testing.T) { | ||
t.Parallel() | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing this you can just use
clock.TickerFunc
, it removes any timing problems in the tests as wellThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that makes more sense