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
18 changes: 12 additions & 6 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ import (
"github.com/coder/serpent"
"github.com/coder/wgtunnel/tunnelsdk"

"github.com/coder/coder/v2/coderd/entitlements"
"github.com/coder/coder/v2/coderd/notifications/reports"
"github.com/coder/coder/v2/coderd/runtimeconfig"
"github.com/coder/coder/v2/coderd/webpush"
"github.com/coder/coder/v2/codersdk/drpcsdk"

"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/cli/clilog"
"github.com/coder/coder/v2/cli/cliui"
Expand All @@ -83,25 +77,31 @@ import (
"github.com/coder/coder/v2/coderd/database/migrations"
"github.com/coder/coder/v2/coderd/database/pubsub"
"github.com/coder/coder/v2/coderd/devtunnel"
"github.com/coder/coder/v2/coderd/entitlements"
"github.com/coder/coder/v2/coderd/externalauth"
"github.com/coder/coder/v2/coderd/gitsshkey"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/jobreaper"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/notifications/reports"
"github.com/coder/coder/v2/coderd/oauthpki"
"github.com/coder/coder/v2/coderd/prometheusmetrics"
"github.com/coder/coder/v2/coderd/prometheusmetrics/insights"
"github.com/coder/coder/v2/coderd/promoauth"
"github.com/coder/coder/v2/coderd/provisionerdserver"
"github.com/coder/coder/v2/coderd/runtimeconfig"
"github.com/coder/coder/v2/coderd/schedule"
"github.com/coder/coder/v2/coderd/telemetry"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/coderd/updatecheck"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/coderd/util/slice"
stringutil "github.com/coder/coder/v2/coderd/util/strings"
"github.com/coder/coder/v2/coderd/webpush"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
"github.com/coder/coder/v2/coderd/workspacestats"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/drpcsdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisioner/terraform"
Expand Down Expand Up @@ -280,6 +280,12 @@ func enablePrometheus(
}
}

provisionerdserverMetrics := provisionerdserver.NewMetrics(logger)
if err := provisionerdserverMetrics.Register(options.PrometheusRegistry); err != nil {
return nil, xerrors.Errorf("failed to register provisionerd_server metrics: %w", err)
}
options.ProvisionerdServerMetrics = provisionerdserverMetrics

//nolint:revive
return ServeHandler(
ctx, logger, promhttp.InstrumentMetricHandler(
Expand Down
3 changes: 3 additions & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ type Options struct {
UpdateAgentMetrics func(ctx context.Context, labels prometheusmetrics.AgentMetricLabels, metrics []*agentproto.Stats_Metric)
StatsBatcher workspacestats.Batcher

ProvisionerdServerMetrics *provisionerdserver.Metrics

// WorkspaceAppAuditSessionTimeout allows changing the timeout for audit
// sessions. Raising or lowering this value will directly affect the write
// load of the audit log table. This is used for testing. Default 1 hour.
Expand Down Expand Up @@ -1930,6 +1932,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
},
api.NotificationsEnqueuer,
&api.PrebuildsReconciler,
api.ProvisionerdServerMetrics,
)
if err != nil {
return nil, err
Expand Down
3 changes: 3 additions & 0 deletions coderd/coderdtest/coderdtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ type Options struct {
OIDCConvertKeyCache cryptokeys.SigningKeycache
Clock quartz.Clock
TelemetryReporter telemetry.Reporter

ProvisionerdServerMetrics *provisionerdserver.Metrics
}

// New constructs a codersdk client connected to an in-memory API instance.
Expand Down Expand Up @@ -604,6 +606,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
Clock: options.Clock,
AppEncryptionKeyCache: options.APIKeyEncryptionCache,
OIDCConvertKeyCache: options.OIDCConvertKeyCache,
ProvisionerdServerMetrics: options.ProvisionerdServerMetrics,
}
}

Expand Down
7 changes: 7 additions & 0 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -2699,6 +2699,13 @@ func (q *querier) GetQuotaConsumedForUser(ctx context.Context, params database.G
return q.db.GetQuotaConsumedForUser(ctx, params)
}

func (q *querier) GetRegularWorkspaceCreateMetrics(ctx context.Context) ([]database.GetRegularWorkspaceCreateMetricsRow, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceWorkspace.All()); err != nil {
return nil, err
}
return q.db.GetRegularWorkspaceCreateMetrics(ctx)
}

func (q *querier) GetReplicaByID(ctx context.Context, id uuid.UUID) (database.Replica, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
return database.Replica{}, err
Expand Down
75 changes: 41 additions & 34 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ func TestAsNoActor(t *testing.T) {
func TestPing(t *testing.T) {
t.Parallel()

db, _ := dbtestutil.NewDB(t)
db := dbmock.NewMockStore(gomock.NewController(t))
db.EXPECT().Wrappers().Times(1).Return([]string{})
db.EXPECT().Ping(gomock.Any()).Times(1).Return(time.Second, nil)
q := dbauthz.New(db, &coderdtest.RecordingAuthorizer{}, slog.Make(), coderdtest.AccessControlStorePointer())
_, err := q.Ping(context.Background())
require.NoError(t, err, "must not error")
Expand All @@ -83,34 +85,39 @@ func TestPing(t *testing.T) {
func TestInTX(t *testing.T) {
t.Parallel()

db, _ := dbtestutil.NewDB(t)
var (
ctrl = gomock.NewController(t)
db = dbmock.NewMockStore(ctrl)
mTx = dbmock.NewMockStore(ctrl) // to record the 'in tx' calls
faker = gofakeit.New(0)
w = testutil.Fake(t, faker, database.Workspace{})
actor = rbac.Subject{
ID: uuid.NewString(),
Roles: rbac.RoleIdentifiers{rbac.RoleOwner()},
Groups: []string{},
Scope: rbac.ScopeAll,
}
ctx = dbauthz.As(context.Background(), actor)
)

db.EXPECT().Wrappers().Times(1).Return([]string{}) // called by dbauthz.New
q := dbauthz.New(db, &coderdtest.RecordingAuthorizer{
Wrapped: (&coderdtest.FakeAuthorizer{}).AlwaysReturn(xerrors.New("custom error")),
}, slog.Make(), coderdtest.AccessControlStorePointer())
actor := rbac.Subject{
ID: uuid.NewString(),
Roles: rbac.RoleIdentifiers{rbac.RoleOwner()},
Groups: []string{},
Scope: rbac.ScopeAll,
}
u := dbgen.User(t, db, database.User{})
o := dbgen.Organization(t, db, database.Organization{})
tpl := dbgen.Template(t, db, database.Template{
CreatedBy: u.ID,
OrganizationID: o.ID,
})
w := dbgen.Workspace(t, db, database.WorkspaceTable{
OwnerID: u.ID,
TemplateID: tpl.ID,
OrganizationID: o.ID,
})
ctx := dbauthz.As(context.Background(), actor)

db.EXPECT().InTx(gomock.Any(), gomock.Any()).Times(1).DoAndReturn(
func(f func(database.Store) error, _ *database.TxOptions) error {
return f(mTx)
},
)
mTx.EXPECT().Wrappers().Times(1).Return([]string{})
mTx.EXPECT().GetWorkspaceByID(gomock.Any(), gomock.Any()).Times(1).Return(w, nil)
err := q.InTx(func(tx database.Store) error {
// The inner tx should use the parent's authz
_, err := tx.GetWorkspaceByID(ctx, w.ID)
return err
}, nil)
require.Error(t, err, "must error")
require.ErrorContains(t, err, "custom error", "must be our custom error")
require.ErrorAs(t, err, &dbauthz.NotAuthorizedError{}, "must be an authorized error")
require.True(t, dbauthz.IsNotAuthorizedError(err), "must be an authorized error")
}
Expand All @@ -120,32 +127,26 @@ func TestNew(t *testing.T) {
t.Parallel()

var (
db, _ = dbtestutil.NewDB(t)
ctrl = gomock.NewController(t)
db = dbmock.NewMockStore(ctrl)
faker = gofakeit.New(0)
rec = &coderdtest.RecordingAuthorizer{
Wrapped: &coderdtest.FakeAuthorizer{},
}
subj = rbac.Subject{}
ctx = dbauthz.As(context.Background(), rbac.Subject{})
)
u := dbgen.User(t, db, database.User{})
org := dbgen.Organization(t, db, database.Organization{})
tpl := dbgen.Template(t, db, database.Template{
OrganizationID: org.ID,
CreatedBy: u.ID,
})
exp := dbgen.Workspace(t, db, database.WorkspaceTable{
OwnerID: u.ID,
OrganizationID: org.ID,
TemplateID: tpl.ID,
})
db.EXPECT().Wrappers().Times(1).Return([]string{}).Times(2) // two calls to New()
exp := testutil.Fake(t, faker, database.Workspace{})
db.EXPECT().GetWorkspaceByID(gomock.Any(), exp.ID).Times(1).Return(exp, nil)
// Double wrap should not cause an actual double wrap. So only 1 rbac call
// should be made.
az := dbauthz.New(db, rec, slog.Make(), coderdtest.AccessControlStorePointer())
az = dbauthz.New(az, rec, slog.Make(), coderdtest.AccessControlStorePointer())

w, err := az.GetWorkspaceByID(ctx, exp.ID)
require.NoError(t, err, "must not error")
require.Equal(t, exp, w.WorkspaceTable(), "must be equal")
require.Equal(t, exp, w, "must be equal")

rec.AssertActor(t, subj, rec.Pair(policy.ActionRead, exp))
require.NoError(t, rec.AllAsserted(), "should only be 1 rbac call")
Expand All @@ -154,6 +155,8 @@ func TestNew(t *testing.T) {
// TestDBAuthzRecursive is a simple test to search for infinite recursion
// bugs. It isn't perfect, and only catches a subset of the possible bugs
// as only the first db call will be made. But it is better than nothing.
// This can be removed when all tests in this package are migrated to
// dbmock as it will immediately detect recursive calls.
func TestDBAuthzRecursive(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
Expand Down Expand Up @@ -2174,6 +2177,10 @@ func (s *MethodTestSuite) TestWorkspace() {
dbm.EXPECT().GetWorkspaceAgentDevcontainersByAgentID(gomock.Any(), agt.ID).Return([]database.WorkspaceAgentDevcontainer{d}, nil).AnyTimes()
check.Args(agt.ID).Asserts(w, policy.ActionRead).Returns([]database.WorkspaceAgentDevcontainer{d})
}))
s.Run("GetRegularWorkspaceCreateMetrics", s.Subtest(func(_ database.Store, check *expects) {
check.Args().
Asserts(rbac.ResourceWorkspace.All(), policy.ActionRead)
}))
}

func (s *MethodTestSuite) TestWorkspacePortSharing() {
Expand Down
7 changes: 7 additions & 0 deletions coderd/database/dbmetrics/querymetrics.go

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

15 changes: 15 additions & 0 deletions coderd/database/dbmock/dbmock.go

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

3 changes: 3 additions & 0 deletions coderd/database/querier.go

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

71 changes: 70 additions & 1 deletion coderd/database/queries.sql.go

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

2 changes: 1 addition & 1 deletion coderd/database/queries/prebuilds.sql
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ HAVING COUNT(*) = @hard_limit::bigint;
SELECT
t.name as template_name,
tvp.name as preset_name,
o.name as organization_name,
o.name as organization_name,
COUNT(*) as created_count,
COUNT(*) FILTER (WHERE pj.job_status = 'failed'::provisioner_job_status) as failed_count,
COUNT(*) FILTER (
Expand Down
Loading
Loading