Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -3878,6 +3878,14 @@ func (q *querier) GetDeploymentID(ctx context.Context) (string, error) {
return q.db.GetDeploymentID(ctx)
}

func (q *querier) GetDeploymentWorkspaceAgentStats(ctx context.Context, createdAt time.Time) (database.GetDeploymentWorkspaceAgentStatsRow, error) {
return q.db.GetDeploymentWorkspaceAgentStats(ctx, createdAt)
}

func (q *querier) GetDeploymentWorkspaceAgentUsageStats(ctx context.Context, createdAt time.Time) (database.GetDeploymentWorkspaceAgentUsageStatsRow, error) {
return q.db.GetDeploymentWorkspaceAgentUsageStats(ctx, createdAt)
}

func (q *querier) GetDeploymentWorkspaceStats(ctx context.Context) (database.GetDeploymentWorkspaceStatsRow, error) {
return q.db.GetDeploymentWorkspaceStats(ctx)
}
Expand Down Expand Up @@ -5611,6 +5619,22 @@ func (q *querier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []
return q.db.GetWorkspaceAgentScriptsByAgentIDs(ctx, ids)
}

func (q *querier) GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentStatsRow, error) {
return q.db.GetWorkspaceAgentStats(ctx, createdAt)
}

func (q *querier) GetWorkspaceAgentStatsAndLabels(ctx context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentStatsAndLabelsRow, error) {
return q.db.GetWorkspaceAgentStatsAndLabels(ctx, createdAt)
}

func (q *querier) GetWorkspaceAgentUsageStats(ctx context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentUsageStatsRow, error) {
return q.db.GetWorkspaceAgentUsageStats(ctx, createdAt)
}

func (q *querier) GetWorkspaceAgentUsageStatsAndLabels(ctx context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentUsageStatsAndLabelsRow, error) {
return q.db.GetWorkspaceAgentUsageStatsAndLabels(ctx, createdAt)
}

func (q *querier) GetWorkspaceAgentsByInstanceID(ctx context.Context, authInstanceID string) ([]database.WorkspaceAgent, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err == nil {
return q.db.GetWorkspaceAgentsByInstanceID(ctx, authInstanceID)
Expand Down
74 changes: 22 additions & 52 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5536,20 +5536,14 @@ func (s *MethodTestSuite) TestSystemFunctions() {
check.Args("foo").Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
}))
s.Run("GetDeploymentWorkspaceAgentStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.GetDeploymentWorkspaceAgentStatsParams{
CreatedAt: time.Time{},
AppFamilies: codersdk.SessionCountAppFamiliesJSON(),
}
dbm.EXPECT().GetDeploymentWorkspaceAgentStats(gomock.Any(), arg).Return(database.GetDeploymentWorkspaceAgentStatsRow{}, nil).AnyTimes()
check.Args(arg).Asserts()
t := time.Time{}
dbm.EXPECT().GetDeploymentWorkspaceAgentStats(gomock.Any(), t).Return(database.GetDeploymentWorkspaceAgentStatsRow{}, nil).AnyTimes()
check.Args(t).Asserts()
}))
s.Run("GetDeploymentWorkspaceAgentUsageStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.GetDeploymentWorkspaceAgentUsageStatsParams{
CreatedAt: time.Time{},
AppFamilies: codersdk.SessionCountAppFamiliesJSON(),
}
dbm.EXPECT().GetDeploymentWorkspaceAgentUsageStats(gomock.Any(), arg).Return(database.GetDeploymentWorkspaceAgentUsageStatsRow{}, nil).AnyTimes()
check.Args(arg).Asserts()
t := time.Time{}
dbm.EXPECT().GetDeploymentWorkspaceAgentUsageStats(gomock.Any(), t).Return(database.GetDeploymentWorkspaceAgentUsageStatsRow{}, nil).AnyTimes()
check.Args(t).Asserts()
}))
s.Run("GetDeploymentWorkspaceStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
dbm.EXPECT().GetDeploymentWorkspaceStats(gomock.Any()).Return(database.GetDeploymentWorkspaceStatsRow{}, nil).AnyTimes()
Expand Down Expand Up @@ -5577,36 +5571,24 @@ func (s *MethodTestSuite) TestSystemFunctions() {
check.Args(arg).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
}))
s.Run("GetWorkspaceAgentStatsAndLabels", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.GetWorkspaceAgentStatsAndLabelsParams{
CreatedAt: time.Time{},
AppFamilies: codersdk.SessionCountAppFamiliesJSON(),
}
dbm.EXPECT().GetWorkspaceAgentStatsAndLabels(gomock.Any(), arg).Return([]database.GetWorkspaceAgentStatsAndLabelsRow{}, nil).AnyTimes()
check.Args(arg).Asserts()
t := time.Time{}
dbm.EXPECT().GetWorkspaceAgentStatsAndLabels(gomock.Any(), t).Return([]database.GetWorkspaceAgentStatsAndLabelsRow{}, nil).AnyTimes()
check.Args(t).Asserts()
}))
s.Run("GetWorkspaceAgentUsageStatsAndLabels", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.GetWorkspaceAgentUsageStatsAndLabelsParams{
CreatedAt: time.Time{},
AppFamilies: codersdk.SessionCountAppFamiliesJSON(),
}
dbm.EXPECT().GetWorkspaceAgentUsageStatsAndLabels(gomock.Any(), arg).Return([]database.GetWorkspaceAgentUsageStatsAndLabelsRow{}, nil).AnyTimes()
check.Args(arg).Asserts()
t := time.Time{}
dbm.EXPECT().GetWorkspaceAgentUsageStatsAndLabels(gomock.Any(), t).Return([]database.GetWorkspaceAgentUsageStatsAndLabelsRow{}, nil).AnyTimes()
check.Args(t).Asserts()
}))
s.Run("GetWorkspaceAgentStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.GetWorkspaceAgentStatsParams{
CreatedAt: time.Time{},
AppFamilies: codersdk.SessionCountAppFamiliesJSON(),
}
dbm.EXPECT().GetWorkspaceAgentStats(gomock.Any(), arg).Return([]database.GetWorkspaceAgentStatsRow{}, nil).AnyTimes()
check.Args(arg).Asserts()
t := time.Time{}
dbm.EXPECT().GetWorkspaceAgentStats(gomock.Any(), t).Return([]database.GetWorkspaceAgentStatsRow{}, nil).AnyTimes()
check.Args(t).Asserts()
}))
s.Run("GetWorkspaceAgentUsageStats", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
arg := database.GetWorkspaceAgentUsageStatsParams{
CreatedAt: time.Time{},
AppFamilies: codersdk.SessionCountAppFamiliesJSON(),
}
dbm.EXPECT().GetWorkspaceAgentUsageStats(gomock.Any(), arg).Return([]database.GetWorkspaceAgentUsageStatsRow{}, nil).AnyTimes()
check.Args(arg).Asserts()
t := time.Time{}
dbm.EXPECT().GetWorkspaceAgentUsageStats(gomock.Any(), t).Return([]database.GetWorkspaceAgentUsageStatsRow{}, nil).AnyTimes()
check.Args(t).Asserts()
}))
s.Run("GetWorkspaceProxyByHostname", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
p := testutil.Fake(s.T(), faker, database.WorkspaceProxy{WildcardHostname: "*.example.com"})
Expand Down Expand Up @@ -7968,8 +7950,8 @@ func TestAsExternalAuthChecker(t *testing.T) {
})
}

// TestSessionCountAppFamiliesRequired ensures the session count queries fail
// loudly when the app family registry is empty, so a forgotten parameter
// TestSessionCountAppFamiliesRequired ensures the queries that take the app
// family registry fail loudly when it is empty, so a forgotten parameter
// surfaces as an error instead of silently dropping every family's sessions
// from usage reporting.
func TestSessionCountAppFamiliesRequired(t *testing.T) {
Expand All @@ -7982,19 +7964,7 @@ func TestSessionCountAppFamiliesRequired(t *testing.T) {
q := dbauthz.New(dbm, &coderdtest.RecordingAuthorizer{Wrapped: &coderdtest.FakeAuthorizer{}}, slog.Make(), coderdtest.AccessControlStorePointer())
ctx := dbauthz.As(context.Background(), coderdtest.RandomRBACSubject())

_, err := q.GetDeploymentWorkspaceAgentStats(ctx, database.GetDeploymentWorkspaceAgentStatsParams{})
require.ErrorContains(t, err, "developer error")
_, err = q.GetDeploymentWorkspaceAgentUsageStats(ctx, database.GetDeploymentWorkspaceAgentUsageStatsParams{})
require.ErrorContains(t, err, "developer error")
_, err = q.GetWorkspaceAgentStats(ctx, database.GetWorkspaceAgentStatsParams{})
require.ErrorContains(t, err, "developer error")
_, err = q.GetWorkspaceAgentStatsAndLabels(ctx, database.GetWorkspaceAgentStatsAndLabelsParams{})
require.ErrorContains(t, err, "developer error")
_, err = q.GetWorkspaceAgentUsageStats(ctx, database.GetWorkspaceAgentUsageStatsParams{})
require.ErrorContains(t, err, "developer error")
_, err = q.GetWorkspaceAgentUsageStatsAndLabels(ctx, database.GetWorkspaceAgentUsageStatsAndLabelsParams{})
require.ErrorContains(t, err, "developer error")
_, err = q.GetTemplateInsightsByTemplate(ctx, database.GetTemplateInsightsByTemplateParams{})
_, err := q.GetTemplateInsightsByTemplate(ctx, database.GetTemplateInsightsByTemplateParams{})
require.ErrorContains(t, err, "developer error")
err = q.UpsertTemplateUsageStats(ctx, nil)
require.ErrorContains(t, err, "developer error")
Expand Down Expand Up @@ -8050,7 +8020,7 @@ func TestSessionCountAppFamiliesMustMatchQueries(t *testing.T) {
q := dbauthz.New(dbm, &coderdtest.RecordingAuthorizer{Wrapped: &coderdtest.FakeAuthorizer{}}, slog.Make(), coderdtest.AccessControlStorePointer())
ctx := dbauthz.As(context.Background(), coderdtest.RandomRBACSubject())

_, err := q.GetDeploymentWorkspaceAgentStats(ctx, database.GetDeploymentWorkspaceAgentStatsParams{AppFamilies: tc.appFamilies})
_, err := q.GetTemplateInsightsByTemplate(ctx, database.GetTemplateInsightsByTemplateParams{AppFamilies: tc.appFamilies})
require.ErrorContains(t, err, tc.errContains)
err = q.UpsertTemplateUsageStats(ctx, tc.appFamilies)
require.ErrorContains(t, err, tc.errContains)
Expand Down
62 changes: 10 additions & 52 deletions coderd/database/dbauthz/sessioncountparams.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ import (
"github.com/coder/coder/v2/codersdk"
)

// The session count read queries take the app family attribution registry as
// a single jsonb parameter. Every query hardcodes one probe per family, so a
// registry that is empty, malformed, or keyed differently from
// codersdk.AttributedAppFamilies would still run and return zero counts for
// the affected families, silently dropping sessions from deployment stats,
// insights, Prometheus, and telemetry. dbauthz wraps every production store,
// including the transaction stores used by the rollup, so validating here
// makes a wrong registry fail the call loudly instead of failing the data
// quietly. These methods override the generated ones in dbauthz.go;
// scripts/dbgen preserves methods defined outside that file.
// The template insights read query and the usage stats rollup take the app
// family attribution registry as a single jsonb parameter. Both hardcode one
// probe per family, so a registry that is empty, malformed, or keyed
// differently from codersdk.AttributedAppFamilies would still run and return
// zero counts for the affected families, silently dropping sessions from
// insights and Prometheus. dbauthz wraps every production store, including
// the transaction stores used by the rollup, so validating here makes a wrong
// registry fail the call loudly instead of failing the data quietly. These
// methods override the generated ones in dbauthz.go; scripts/dbgen preserves
// methods defined outside that file.

// validateSessionCountAppFamilies checks that the registry has exactly the
// families the queries probe, each with at least one app name.
Expand Down Expand Up @@ -54,48 +54,6 @@ func validateSessionCountAppFamilies(appFamilies json.RawMessage) error {
return nil
}

func (q *querier) GetDeploymentWorkspaceAgentStats(ctx context.Context, arg database.GetDeploymentWorkspaceAgentStatsParams) (database.GetDeploymentWorkspaceAgentStatsRow, error) {
if err := validateSessionCountAppFamilies(arg.AppFamilies); err != nil {
return database.GetDeploymentWorkspaceAgentStatsRow{}, err
}
return q.db.GetDeploymentWorkspaceAgentStats(ctx, arg)
}

func (q *querier) GetDeploymentWorkspaceAgentUsageStats(ctx context.Context, arg database.GetDeploymentWorkspaceAgentUsageStatsParams) (database.GetDeploymentWorkspaceAgentUsageStatsRow, error) {
if err := validateSessionCountAppFamilies(arg.AppFamilies); err != nil {
return database.GetDeploymentWorkspaceAgentUsageStatsRow{}, err
}
return q.db.GetDeploymentWorkspaceAgentUsageStats(ctx, arg)
}

func (q *querier) GetWorkspaceAgentStats(ctx context.Context, arg database.GetWorkspaceAgentStatsParams) ([]database.GetWorkspaceAgentStatsRow, error) {
if err := validateSessionCountAppFamilies(arg.AppFamilies); err != nil {
return nil, err
}
return q.db.GetWorkspaceAgentStats(ctx, arg)
}

func (q *querier) GetWorkspaceAgentStatsAndLabels(ctx context.Context, arg database.GetWorkspaceAgentStatsAndLabelsParams) ([]database.GetWorkspaceAgentStatsAndLabelsRow, error) {
if err := validateSessionCountAppFamilies(arg.AppFamilies); err != nil {
return nil, err
}
return q.db.GetWorkspaceAgentStatsAndLabels(ctx, arg)
}

func (q *querier) GetWorkspaceAgentUsageStats(ctx context.Context, arg database.GetWorkspaceAgentUsageStatsParams) ([]database.GetWorkspaceAgentUsageStatsRow, error) {
if err := validateSessionCountAppFamilies(arg.AppFamilies); err != nil {
return nil, err
}
return q.db.GetWorkspaceAgentUsageStats(ctx, arg)
}

func (q *querier) GetWorkspaceAgentUsageStatsAndLabels(ctx context.Context, arg database.GetWorkspaceAgentUsageStatsAndLabelsParams) ([]database.GetWorkspaceAgentUsageStatsAndLabelsRow, error) {
if err := validateSessionCountAppFamilies(arg.AppFamilies); err != nil {
return nil, err
}
return q.db.GetWorkspaceAgentUsageStatsAndLabels(ctx, arg)
}

func (q *querier) GetTemplateInsightsByTemplate(ctx context.Context, arg database.GetTemplateInsightsByTemplateParams) ([]database.GetTemplateInsightsByTemplateRow, error) {
// Only used by prometheus metrics collector. No need to check update template perms.
if err := q.authorizeContext(ctx, policy.ActionViewInsights, rbac.ResourceTemplate); err != nil {
Expand Down
12 changes: 6 additions & 6 deletions coderd/database/dbmetrics/querymetrics.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading