chore: wire up usage tracking for managed agents (#19096)

Wires up the usage collector and publisher to coderd.

Relates to coder/internal#814
This commit is contained in:
Dean Sheather
2025-08-20 23:38:09 +10:00
committed by GitHub
parent dd867bd743
commit 6eb02d1c2a
43 changed files with 540 additions and 346 deletions
+16 -12
View File
@@ -213,6 +213,8 @@ var (
// Provisionerd creates workspaces resources monitor
rbac.ResourceWorkspaceAgentResourceMonitor.Type: {policy.ActionCreate},
rbac.ResourceWorkspaceAgentDevcontainers.Type: {policy.ActionCreate},
// Provisionerd creates usage events
rbac.ResourceUsageEvent.Type: {policy.ActionCreate},
}),
Org: map[string][]rbac.Permission{},
User: []rbac.Permission{},
@@ -510,17 +512,19 @@ var (
Scope: rbac.ScopeAll,
}.WithCachedASTValue()
subjectUsageTracker = rbac.Subject{
Type: rbac.SubjectTypeUsageTracker,
FriendlyName: "Usage Tracker",
subjectUsagePublisher = rbac.Subject{
Type: rbac.SubjectTypeUsagePublisher,
FriendlyName: "Usage Publisher",
ID: uuid.Nil.String(),
Roles: rbac.Roles([]rbac.Role{
{
Identifier: rbac.RoleIdentifier{Name: "usage-tracker"},
DisplayName: "Usage Tracker",
Identifier: rbac.RoleIdentifier{Name: "usage-publisher"},
DisplayName: "Usage Publisher",
Site: rbac.Permissions(map[string][]policy.Action{
rbac.ResourceLicense.Type: {policy.ActionRead},
rbac.ResourceUsageEvent.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
rbac.ResourceLicense.Type: {policy.ActionRead},
// The usage publisher doesn't create events, just
// reads/processes them.
rbac.ResourceUsageEvent.Type: {policy.ActionRead, policy.ActionUpdate},
}),
Org: map[string][]rbac.Permission{},
User: []rbac.Permission{},
@@ -604,10 +608,10 @@ func AsFileReader(ctx context.Context) context.Context {
return As(ctx, subjectFileReader)
}
// AsUsageTracker returns a context with an actor that has permissions required
// for creating, reading, and updating usage events.
func AsUsageTracker(ctx context.Context) context.Context {
return As(ctx, subjectUsageTracker)
// AsUsagePublisher returns a context with an actor that has permissions
// required for creating, reading, and updating usage events.
func AsUsagePublisher(ctx context.Context) context.Context {
return As(ctx, subjectUsagePublisher)
}
var AsRemoveActor = rbac.Subject{
@@ -3038,7 +3042,7 @@ func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTe
}
func (q *querier) GetUnexpiredLicenses(ctx context.Context) ([]database.License, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceLicense); err != nil {
return nil, err
}
return q.db.GetUnexpiredLicenses(ctx)
+12 -3
View File
@@ -758,6 +758,18 @@ func (s *MethodTestSuite) TestLicense() {
check.Args().Asserts(l, policy.ActionRead).
Returns([]database.License{l})
}))
s.Run("GetUnexpiredLicenses", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
l := database.License{
ID: 1,
Exp: time.Now().Add(time.Hour * 24 * 30),
UUID: uuid.New(),
}
db.EXPECT().GetUnexpiredLicenses(gomock.Any()).
Return([]database.License{l}, nil).
AnyTimes()
check.Args().Asserts(rbac.ResourceLicense, policy.ActionRead).
Returns([]database.License{l})
}))
s.Run("InsertLicense", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertLicenseParams{}).
Asserts(rbac.ResourceLicense, policy.ActionCreate)
@@ -3770,9 +3782,6 @@ func (s *MethodTestSuite) TestSystemFunctions() {
s.Run("GetActiveUserCount", s.Subtest(func(db database.Store, check *expects) {
check.Args(false).Asserts(rbac.ResourceSystem, policy.ActionRead).Returns(int64(0))
}))
s.Run("GetUnexpiredLicenses", s.Subtest(func(db database.Store, check *expects) {
check.Args().Asserts(rbac.ResourceSystem, policy.ActionRead)
}))
s.Run("GetAuthorizationUserRoles", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})
check.Args(u.ID).Asserts(rbac.ResourceSystem, policy.ActionRead)