From 1979cde773db922685c095e8b8019f8454aaa581 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 09:58:25 -0600 Subject: [PATCH 01/10] feat: Implement view for workspace builds to include rbac info Removes the need to fetch the workspace to run an rbac check. --- coderd/database/dump.sql | 47 ++++++++++----- .../000101_workspace_build_view.down.sql | 1 + .../000101_workspace_build_view.up.sql | 14 +++++ coderd/database/models.go | 18 ++++++ coderd/database/querier.go | 8 +-- coderd/database/queries.sql.go | 60 ++++++++++++------- coderd/database/queries/workspacebuilds.sql | 22 +++---- coderd/database/sqlc.yaml | 2 + 8 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 coderd/database/migrations/000101_workspace_build_view.down.sql create mode 100644 coderd/database/migrations/000101_workspace_build_view.up.sql diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 29316b88633c6..69186a82e012a 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -561,6 +561,39 @@ CREATE TABLE workspace_builds ( daily_cost integer DEFAULT 0 NOT NULL ); +CREATE TABLE workspaces ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + owner_id uuid NOT NULL, + organization_id uuid NOT NULL, + template_id uuid NOT NULL, + deleted boolean DEFAULT false NOT NULL, + name character varying(64) NOT NULL, + autostart_schedule text, + ttl bigint, + last_used_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL +); + +CREATE VIEW workspace_builds_rbac AS + SELECT workspace_builds.id, + workspace_builds.created_at, + workspace_builds.updated_at, + workspace_builds.workspace_id, + workspace_builds.template_version_id, + workspace_builds.build_number, + workspace_builds.transition, + workspace_builds.initiator_id, + workspace_builds.provisioner_state, + workspace_builds.job_id, + workspace_builds.deadline, + workspace_builds.reason, + workspace_builds.daily_cost, + workspaces.organization_id, + workspaces.owner_id AS workspace_owner_id + FROM (public.workspace_builds + JOIN workspaces ON ((workspace_builds.workspace_id = workspaces.id))); + CREATE TABLE workspace_resource_metadata ( workspace_resource_id uuid NOT NULL, key character varying(1024) NOT NULL, @@ -591,20 +624,6 @@ CREATE TABLE workspace_resources ( daily_cost integer DEFAULT 0 NOT NULL ); -CREATE TABLE workspaces ( - id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - owner_id uuid NOT NULL, - organization_id uuid NOT NULL, - template_id uuid NOT NULL, - deleted boolean DEFAULT false NOT NULL, - name character varying(64) NOT NULL, - autostart_schedule text, - ttl bigint, - last_used_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL -); - ALTER TABLE ONLY licenses ALTER COLUMN id SET DEFAULT nextval('licenses_id_seq'::regclass); ALTER TABLE ONLY provisioner_job_logs ALTER COLUMN id SET DEFAULT nextval('provisioner_job_logs_id_seq'::regclass); diff --git a/coderd/database/migrations/000101_workspace_build_view.down.sql b/coderd/database/migrations/000101_workspace_build_view.down.sql new file mode 100644 index 0000000000000..a8ce5434bc705 --- /dev/null +++ b/coderd/database/migrations/000101_workspace_build_view.down.sql @@ -0,0 +1 @@ +DROP VIEW workspace_builds_rbac; diff --git a/coderd/database/migrations/000101_workspace_build_view.up.sql b/coderd/database/migrations/000101_workspace_build_view.up.sql new file mode 100644 index 0000000000000..5c7a580730d52 --- /dev/null +++ b/coderd/database/migrations/000101_workspace_build_view.up.sql @@ -0,0 +1,14 @@ +BEGIN; +-- workspace_builds_rbac includes the linked workspace information +-- required to perform RBAC checks on workspace builds without needing +-- to fetch the workspace. +CREATE VIEW workspace_builds_rbac AS +SELECT + workspace_builds.*, + workspaces.organization_id AS organization_id, + workspaces.owner_id AS workspace_owner_id +FROM + workspace_builds +INNER JOIN + workspaces ON workspace_builds.workspace_id = workspaces.id; +COMMIT; diff --git a/coderd/database/models.go b/coderd/database/models.go index 21e22566fc40b..e9f6c9619d598 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1590,6 +1590,8 @@ type WorkspaceBuild struct { Deadline time.Time `db:"deadline" json:"deadline"` Reason BuildReason `db:"reason" json:"reason"` DailyCost int32 `db:"daily_cost" json:"daily_cost"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + WorkspaceOwnerID uuid.UUID `db:"workspace_owner_id" json:"workspace_owner_id"` } type WorkspaceBuildParameter struct { @@ -1600,6 +1602,22 @@ type WorkspaceBuildParameter struct { Value string `db:"value" json:"value"` } +type WorkspaceBuildThin struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"` + TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"` + BuildNumber int32 `db:"build_number" json:"build_number"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"` + ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Deadline time.Time `db:"deadline" json:"deadline"` + Reason BuildReason `db:"reason" json:"reason"` + DailyCost int32 `db:"daily_cost" json:"daily_cost"` +} + type WorkspaceResource struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` diff --git a/coderd/database/querier.go b/coderd/database/querier.go index a2849211b60be..cdbfa5921a1fe 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -55,7 +55,7 @@ type sqlcQuerier interface { GetGroupsByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]Group, error) GetLastUpdateCheck(ctx context.Context) (string, error) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error) - GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error) + GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuildThin, error) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error) GetLicenseByID(ctx context.Context, id int32) (License, error) GetLicenses(ctx context.Context) ([]License, error) @@ -169,7 +169,7 @@ type sqlcQuerier interface { InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error) - InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) + InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuildThin, error) InsertWorkspaceBuildParameters(ctx context.Context, arg InsertWorkspaceBuildParametersParams) error InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error) @@ -205,8 +205,8 @@ type sqlcQuerier interface { UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error UpdateWorkspaceAppHealthByID(ctx context.Context, arg UpdateWorkspaceAppHealthByIDParams) error UpdateWorkspaceAutostart(ctx context.Context, arg UpdateWorkspaceAutostartParams) error - UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) (WorkspaceBuild, error) - UpdateWorkspaceBuildCostByID(ctx context.Context, arg UpdateWorkspaceBuildCostByIDParams) (WorkspaceBuild, error) + UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) (WorkspaceBuildThin, error) + UpdateWorkspaceBuildCostByID(ctx context.Context, arg UpdateWorkspaceBuildCostByIDParams) (WorkspaceBuildThin, error) UpdateWorkspaceDeletedByID(ctx context.Context, arg UpdateWorkspaceDeletedByIDParams) error UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index c2022fa98f502..b972c3d69bb06 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -5765,9 +5765,9 @@ func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg Ins const getLatestWorkspaceBuildByWorkspaceID = `-- name: GetLatestWorkspaceBuildByWorkspaceID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM - workspace_builds + workspace_builds_rbac WHERE workspace_id = $1 ORDER BY @@ -5793,6 +5793,8 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, w &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ) return i, err } @@ -5812,15 +5814,15 @@ JOIN ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number ` -func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error) { +func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuildThin, error) { rows, err := q.db.QueryContext(ctx, getLatestWorkspaceBuilds) if err != nil { return nil, err } defer rows.Close() - var items []WorkspaceBuild + var items []WorkspaceBuildThin for rows.Next() { - var i WorkspaceBuild + var i WorkspaceBuildThin if err := rows.Scan( &i.ID, &i.CreatedAt, @@ -5850,19 +5852,19 @@ func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceB } const getLatestWorkspaceBuildsByWorkspaceIDs = `-- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many -SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost +SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.organization_id, wb.workspace_owner_id FROM ( SELECT workspace_id, MAX(build_number) as max_build_number FROM - workspace_builds + workspace_builds WHERE workspace_id = ANY($1 :: uuid [ ]) GROUP BY workspace_id ) m JOIN - workspace_builds wb + workspace_builds_rbac wb ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number ` @@ -5889,6 +5891,8 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ); err != nil { return nil, err } @@ -5905,9 +5909,9 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, const getWorkspaceBuildByID = `-- name: GetWorkspaceBuildByID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM - workspace_builds + workspace_builds_rbac WHERE id = $1 LIMIT @@ -5931,15 +5935,17 @@ func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (W &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ) return i, err } const getWorkspaceBuildByJobID = `-- name: GetWorkspaceBuildByJobID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM - workspace_builds + workspace_builds_rbac WHERE job_id = $1 LIMIT @@ -5963,15 +5969,17 @@ func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UU &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ) return i, err } const getWorkspaceBuildByWorkspaceIDAndBuildNumber = `-- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM - workspace_builds + workspace_builds_rbac WHERE workspace_id = $1 AND build_number = $2 @@ -5999,15 +6007,17 @@ func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Co &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ) return i, err } const getWorkspaceBuildsByWorkspaceID = `-- name: GetWorkspaceBuildsByWorkspaceID :many SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM - workspace_builds + workspace_builds_rbac WHERE workspace_builds.workspace_id = $1 AND workspace_builds.created_at > $2 @@ -6074,6 +6084,8 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg Ge &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ); err != nil { return nil, err } @@ -6089,7 +6101,7 @@ func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg Ge } const getWorkspaceBuildsCreatedAfter = `-- name: GetWorkspaceBuildsCreatedAfter :many -SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost FROM workspace_builds WHERE created_at > $1 +SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM workspace_builds_rbac WHERE created_at > $1 ` func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceBuild, error) { @@ -6115,6 +6127,8 @@ func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, created &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ); err != nil { return nil, err } @@ -6164,7 +6178,7 @@ type InsertWorkspaceBuildParams struct { Reason BuildReason `db:"reason" json:"reason"` } -func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) { +func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuildThin, error) { row := q.db.QueryRowContext(ctx, insertWorkspaceBuild, arg.ID, arg.CreatedAt, @@ -6179,7 +6193,7 @@ func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspa arg.Deadline, arg.Reason, ) - var i WorkspaceBuild + var i WorkspaceBuildThin err := row.Scan( &i.ID, &i.CreatedAt, @@ -6216,14 +6230,14 @@ type UpdateWorkspaceBuildByIDParams struct { Deadline time.Time `db:"deadline" json:"deadline"` } -func (q *sqlQuerier) UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) (WorkspaceBuild, error) { +func (q *sqlQuerier) UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) (WorkspaceBuildThin, error) { row := q.db.QueryRowContext(ctx, updateWorkspaceBuildByID, arg.ID, arg.UpdatedAt, arg.ProvisionerState, arg.Deadline, ) - var i WorkspaceBuild + var i WorkspaceBuildThin err := row.Scan( &i.ID, &i.CreatedAt, @@ -6256,9 +6270,9 @@ type UpdateWorkspaceBuildCostByIDParams struct { DailyCost int32 `db:"daily_cost" json:"daily_cost"` } -func (q *sqlQuerier) UpdateWorkspaceBuildCostByID(ctx context.Context, arg UpdateWorkspaceBuildCostByIDParams) (WorkspaceBuild, error) { +func (q *sqlQuerier) UpdateWorkspaceBuildCostByID(ctx context.Context, arg UpdateWorkspaceBuildCostByIDParams) (WorkspaceBuildThin, error) { row := q.db.QueryRowContext(ctx, updateWorkspaceBuildCostByID, arg.ID, arg.DailyCost) - var i WorkspaceBuild + var i WorkspaceBuildThin err := row.Scan( &i.ID, &i.CreatedAt, diff --git a/coderd/database/queries/workspacebuilds.sql b/coderd/database/queries/workspacebuilds.sql index 30658634da4e0..6466093534dbe 100644 --- a/coderd/database/queries/workspacebuilds.sql +++ b/coderd/database/queries/workspacebuilds.sql @@ -2,7 +2,7 @@ SELECT * FROM - workspace_builds + workspace_builds_rbac WHERE id = $1 LIMIT @@ -12,20 +12,20 @@ LIMIT SELECT * FROM - workspace_builds + workspace_builds_rbac WHERE job_id = $1 LIMIT 1; -- name: GetWorkspaceBuildsCreatedAfter :many -SELECT * FROM workspace_builds WHERE created_at > $1; +SELECT * FROM workspace_builds_rbac WHERE created_at > $1; -- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one SELECT * FROM - workspace_builds + workspace_builds_rbac WHERE workspace_id = $1 AND build_number = $2; @@ -34,11 +34,11 @@ WHERE SELECT * FROM - workspace_builds + workspace_builds_rbac WHERE workspace_builds.workspace_id = $1 AND workspace_builds.created_at > @since - AND CASE + AND CASE -- This allows using the last element on a page as effectively a cursor. -- This is an important option for scripts that need to paginate without -- duplicating or missing data. @@ -65,15 +65,15 @@ LIMIT -- name: GetLatestWorkspaceBuildByWorkspaceID :one SELECT - * + * FROM - workspace_builds + workspace_builds_rbac WHERE - workspace_id = $1 + workspace_id = $1 ORDER BY build_number desc LIMIT - 1; + 1; -- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many SELECT wb.* @@ -88,7 +88,7 @@ FROM ( workspace_id ) m JOIN - workspace_builds wb + workspace_builds_rbac wb ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number; -- name: GetLatestWorkspaceBuilds :many diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index e708a6c4ca065..98c4308da584b 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -20,6 +20,8 @@ overrides: go_type: type: "TemplateACL" rename: + workspace_builds_rbac: WorkspaceBuild + workspace_build: WorkspaceBuildThin api_key: APIKey api_key_scope: APIKeyScope api_key_scope_all: APIKeyScopeAll From 4c0cd840ca0b9b7998c998a8da79801bfa2fd34e Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 11:03:45 -0600 Subject: [PATCH 02/10] chore: Use workspace build as RBAC object --- coderd/database/dbauthz/querier.go | 44 ++++---------- coderd/database/dbauthz/querier_test.go | 42 ++++++------- coderd/database/dbauthz/system.go | 2 +- coderd/database/dbfake/databasefake.go | 67 +++++++++++++-------- coderd/database/dbgen/generator.go | 37 ++++++++---- coderd/database/modelmethods.go | 50 +++++++++++++++ coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 30 ++++----- coderd/database/queries/workspacebuilds.sql | 6 +- coderd/workspacebuilds.go | 5 +- coderd/workspaces.go | 3 +- 11 files changed, 174 insertions(+), 114 deletions(-) diff --git a/coderd/database/dbauthz/querier.go b/coderd/database/dbauthz/querier.go index 691f680e42feb..91ca67fc54372 100644 --- a/coderd/database/dbauthz/querier.go +++ b/coderd/database/dbauthz/querier.go @@ -1179,10 +1179,7 @@ func (q *querier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesP } func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) { - if _, err := q.GetWorkspaceByID(ctx, workspaceID); err != nil { - return database.WorkspaceBuild{}, err - } - return q.db.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspaceID) + return fetch(q.log, q.auth, q.db.GetLatestWorkspaceBuildByWorkspaceID)(ctx, workspaceID) } func (q *querier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceBuild, error) { @@ -1320,34 +1317,15 @@ func (q *querier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUI } func (q *querier) GetWorkspaceBuildByID(ctx context.Context, buildID uuid.UUID) (database.WorkspaceBuild, error) { - build, err := q.db.GetWorkspaceBuildByID(ctx, buildID) - if err != nil { - return database.WorkspaceBuild{}, err - } - if _, err := q.GetWorkspaceByID(ctx, build.WorkspaceID); err != nil { - return database.WorkspaceBuild{}, err - } - return build, nil + return fetch(q.log, q.auth, q.db.GetWorkspaceBuildByID)(ctx, buildID) } func (q *querier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (database.WorkspaceBuild, error) { - build, err := q.db.GetWorkspaceBuildByJobID(ctx, jobID) - if err != nil { - return database.WorkspaceBuild{}, err - } - // Authorized fetch - _, err = q.GetWorkspaceByID(ctx, build.WorkspaceID) - if err != nil { - return database.WorkspaceBuild{}, err - } - return build, nil + return fetch(q.log, q.auth, q.db.GetWorkspaceBuildByJobID)(ctx, jobID) } func (q *querier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuild, error) { - if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil { - return database.WorkspaceBuild{}, err - } - return q.db.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx, arg) + return fetch(q.log, q.auth, q.db.GetWorkspaceBuildByWorkspaceIDAndBuildNumber)(ctx, arg) } func (q *querier) GetWorkspaceBuildParameters(ctx context.Context, workspaceBuildID uuid.UUID) ([]database.WorkspaceBuildParameter, error) { @@ -1475,10 +1453,10 @@ func (q *querier) InsertWorkspace(ctx context.Context, arg database.InsertWorksp return insert(q.log, q.auth, obj, q.db.InsertWorkspace)(ctx, arg) } -func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) (database.WorkspaceBuild, error) { +func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) (database.WorkspaceBuildThin, error) { w, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceID) if err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } var action rbac.Action = rbac.ActionUpdate @@ -1487,7 +1465,7 @@ func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertW } if err = q.authorizeContext(ctx, action, w); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } return q.db.InsertWorkspaceBuild(ctx, arg) @@ -1563,19 +1541,19 @@ func (q *querier) UpdateWorkspaceAutostart(ctx context.Context, arg database.Upd return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceAutostart)(ctx, arg) } -func (q *querier) UpdateWorkspaceBuildByID(ctx context.Context, arg database.UpdateWorkspaceBuildByIDParams) (database.WorkspaceBuild, error) { +func (q *querier) UpdateWorkspaceBuildByID(ctx context.Context, arg database.UpdateWorkspaceBuildByIDParams) (database.WorkspaceBuildThin, error) { build, err := q.db.GetWorkspaceBuildByID(ctx, arg.ID) if err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID) if err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace.RBACObject()) if err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } return q.db.UpdateWorkspaceBuildByID(ctx, arg) diff --git a/coderd/database/dbauthz/querier_test.go b/coderd/database/dbauthz/querier_test.go index 0f7e7c4ffa45d..9649a8427e3f7 100644 --- a/coderd/database/dbauthz/querier_test.go +++ b/coderd/database/dbauthz/querier_test.go @@ -946,16 +946,16 @@ func (s *MethodTestSuite) TestWorkspace() { s.Run("GetLatestWorkspaceBuildByWorkspaceID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}) - check.Args(ws.ID).Asserts(ws, rbac.ActionRead).Returns(b) + check.Args(ws.ID).Asserts(ws, rbac.ActionRead).Returns(b.WithWorkspace(ws)) })) s.Run("GetLatestWorkspaceBuildsByWorkspaceIDs", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}) + b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}).WithWorkspace(ws) check.Args([]uuid.UUID{ws.ID}).Asserts(ws, rbac.ActionRead).Returns(slice.New(b)) })) s.Run("GetWorkspaceAgentByID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) check.Args(agt.ID).Asserts(ws, rbac.ActionRead).Returns(agt) @@ -969,7 +969,7 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceAgentsByResourceIDs", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) check.Args([]uuid.UUID{res.ID}).Asserts(ws, rbac.ActionRead). @@ -977,7 +977,7 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("UpdateWorkspaceAgentLifecycleStateByID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) check.Args(database.UpdateWorkspaceAgentLifecycleStateByIDParams{ @@ -987,7 +987,7 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("UpdateWorkspaceAgentStartupByID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) check.Args(database.UpdateWorkspaceAgentStartupByIDParams{ @@ -996,7 +996,7 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceAppByAgentIDAndSlug", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) app := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: agt.ID}) @@ -1008,7 +1008,7 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceAppsByAgentID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) a := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: agt.ID}) @@ -1018,13 +1018,13 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceAppsByAgentIDs", s.Subtest(func(db database.Store, check *expects) { aWs := dbgen.Workspace(s.T(), db, database.Workspace{}) - aBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: aWs.ID, JobID: uuid.New()}) + aBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: aWs.ID, JobID: uuid.New()}).WithWorkspace(aWs) aRes := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: aBuild.JobID}) aAgt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: aRes.ID}) a := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: aAgt.ID}) bWs := dbgen.Workspace(s.T(), db, database.Workspace{}) - bBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: bWs.ID, JobID: uuid.New()}) + bBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: bWs.ID, JobID: uuid.New()}).WithWorkspace(bWs) bRes := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: bBuild.JobID}) bAgt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: bRes.ID}) b := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: bAgt.ID}) @@ -1035,17 +1035,17 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceBuildByID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}).WithWorkspace(ws) check.Args(build.ID).Asserts(ws, rbac.ActionRead).Returns(build) })) s.Run("GetWorkspaceBuildByJobID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID}).WithWorkspace(ws) check.Args(build.JobID).Asserts(ws, rbac.ActionRead).Returns(build) })) s.Run("GetWorkspaceBuildByWorkspaceIDAndBuildNumber", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 10}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 10}).WithWorkspace(ws) check.Args(database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams{ WorkspaceID: ws.ID, BuildNumber: build.BuildNumber, @@ -1059,14 +1059,14 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceBuildsByWorkspaceID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - _ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 1}) - _ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 2}) - _ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 3}) + _ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 1}).WithWorkspace(ws) + _ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 2}).WithWorkspace(ws) + _ = dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 3}).WithWorkspace(ws) check.Args(database.GetWorkspaceBuildsByWorkspaceIDParams{WorkspaceID: ws.ID}).Asserts(ws, rbac.ActionRead) // ordering })) s.Run("GetWorkspaceByAgentID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) check.Args(agt.ID).Asserts(ws, rbac.ActionRead).Returns(ws) @@ -1081,14 +1081,14 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("GetWorkspaceResourceByID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) _ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild}) res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) check.Args(res.ID).Asserts(ws, rbac.ActionRead).Returns(res) })) s.Run("GetWorkspaceResourceMetadataByResourceIDs", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) _ = dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild}) a := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) b := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) @@ -1097,7 +1097,7 @@ func (s *MethodTestSuite) TestWorkspace() { })) s.Run("Build/GetWorkspaceResourcesByJobID", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) job := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild}) check.Args(job.ID).Asserts(ws, rbac.ActionRead).Returns([]database.WorkspaceResource{}) })) @@ -1113,7 +1113,7 @@ func (s *MethodTestSuite) TestWorkspace() { tJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: v.JobID, Type: database.ProvisionerJobTypeTemplateVersionImport}) ws := dbgen.Workspace(s.T(), db, database.Workspace{}) - build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}).WithWorkspace(ws) wJob := dbgen.ProvisionerJob(s.T(), db, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild}) check.Args([]uuid.UUID{tJob.ID, wJob.ID}).Asserts(v.RBACObject(tpl), rbac.ActionRead, ws, rbac.ActionRead).Returns([]database.WorkspaceResource{}) })) diff --git a/coderd/database/dbauthz/system.go b/coderd/database/dbauthz/system.go index 5baf6ad7604eb..f0f01d4bb282e 100644 --- a/coderd/database/dbauthz/system.go +++ b/coderd/database/dbauthz/system.go @@ -112,7 +112,7 @@ func (q *querier) GetDeploymentDAUs(ctx context.Context) ([]database.GetDeployme } // UpdateWorkspaceBuildCostByID is used by the provisioning system to update the cost of a workspace build. -func (q *querier) UpdateWorkspaceBuildCostByID(ctx context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuild, error) { +func (q *querier) UpdateWorkspaceBuildCostByID(ctx context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuildThin, error) { return q.db.UpdateWorkspaceBuildCostByID(ctx, arg) } diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go index 7c1ea4b3f4abd..a84a57b71041a 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -60,7 +60,7 @@ func New() database.Store { provisionerJobs: make([]database.ProvisionerJob, 0), templateVersions: make([]database.TemplateVersion, 0), templates: make([]database.Template, 0), - workspaceBuilds: make([]database.WorkspaceBuild, 0), + workspaceBuilds: make([]database.WorkspaceBuildThin, 0), workspaceApps: make([]database.WorkspaceApp, 0), workspaces: make([]database.Workspace, 0), licenses: make([]database.License, 0), @@ -118,7 +118,7 @@ type data struct { templates []database.Template workspaceAgents []database.WorkspaceAgent workspaceApps []database.WorkspaceApp - workspaceBuilds []database.WorkspaceBuild + workspaceBuilds []database.WorkspaceBuildThin workspaceBuildParameters []database.WorkspaceBuildParameter workspaceResourceMetadata []database.WorkspaceResourceMetadatum workspaceResources []database.WorkspaceResource @@ -1208,7 +1208,7 @@ func (q *fakeQuerier) GetWorkspaceByAgentID(_ context.Context, agentID uuid.UUID return database.Workspace{}, sql.ErrNoRows } - var build database.WorkspaceBuild + var build database.WorkspaceBuildThin for _, _build := range q.workspaceBuilds { if _build.JobID == resource.JobID { build = _build @@ -1322,13 +1322,28 @@ func (q *fakeQuerier) GetWorkspaceAppsByAgentIDs(_ context.Context, ids []uuid.U return apps, nil } +// expandWorkspaceThins must be called from a locked context. +func (q *fakeQuerier) expandWorkspaceThins(thins []database.WorkspaceBuildThin) []database.WorkspaceBuild { + cpy := make([]database.WorkspaceBuild, 0, len(thins)) + for _, thin := range thins { + cpy = append(cpy, q.expandWorkspaceThin(thin)) + } + return cpy +} + +// expandWorkspaceThin must be called from a locked context. +func (q *fakeQuerier) expandWorkspaceThin(thin database.WorkspaceBuildThin) database.WorkspaceBuild { + w, _ := q.GetWorkspaceByID(context.Background(), thin.WorkspaceID) + return thin.Expand(w.OrganizationID, w.OwnerID) +} + func (q *fakeQuerier) GetWorkspaceBuildByID(_ context.Context, id uuid.UUID) (database.WorkspaceBuild, error) { q.mutex.RLock() defer q.mutex.RUnlock() for _, history := range q.workspaceBuilds { if history.ID == id { - return history, nil + return q.expandWorkspaceThin(history), nil } } return database.WorkspaceBuild{}, sql.ErrNoRows @@ -1340,7 +1355,7 @@ func (q *fakeQuerier) GetWorkspaceBuildByJobID(_ context.Context, jobID uuid.UUI for _, build := range q.workspaceBuilds { if build.JobID == jobID { - return build, nil + return q.expandWorkspaceThin(build), nil } } return database.WorkspaceBuild{}, sql.ErrNoRows @@ -1350,7 +1365,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(_ context.Context, wo q.mutex.RLock() defer q.mutex.RUnlock() - var row database.WorkspaceBuild + var row database.WorkspaceBuildThin var buildNum int32 = -1 for _, workspaceBuild := range q.workspaceBuilds { if workspaceBuild.WorkspaceID == workspaceID && workspaceBuild.BuildNumber > buildNum { @@ -1361,14 +1376,14 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(_ context.Context, wo if buildNum == -1 { return database.WorkspaceBuild{}, sql.ErrNoRows } - return row, nil + return q.expandWorkspaceThin(row), nil } func (q *fakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.WorkspaceBuild, error) { q.mutex.RLock() defer q.mutex.RUnlock() - builds := make(map[uuid.UUID]database.WorkspaceBuild) + builds := make(map[uuid.UUID]database.WorkspaceBuildThin) buildNumbers := make(map[uuid.UUID]int32) for _, workspaceBuild := range q.workspaceBuilds { id := workspaceBuild.WorkspaceID @@ -1381,7 +1396,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.Wo for i, n := range buildNumbers { if n > 0 { b := builds[i] - returnBuilds = append(returnBuilds, b) + returnBuilds = append(returnBuilds, q.expandWorkspaceThin(b)) } } if len(returnBuilds) == 0 { @@ -1394,7 +1409,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context, q.mutex.RLock() defer q.mutex.RUnlock() - builds := make(map[uuid.UUID]database.WorkspaceBuild) + builds := make(map[uuid.UUID]database.WorkspaceBuildThin) buildNumbers := make(map[uuid.UUID]int32) for _, workspaceBuild := range q.workspaceBuilds { for _, id := range ids { @@ -1408,7 +1423,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context, for i, n := range buildNumbers { if n > 0 { b := builds[i] - returnBuilds = append(returnBuilds, b) + returnBuilds = append(returnBuilds, q.expandWorkspaceThin(b)) } } if len(returnBuilds) == 0 { @@ -1427,7 +1442,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, q.mutex.RLock() defer q.mutex.RUnlock() - history := make([]database.WorkspaceBuild, 0) + history := make([]database.WorkspaceBuildThin, 0) for _, workspaceBuild := range q.workspaceBuilds { if workspaceBuild.CreatedAt.Before(params.Since) { continue @@ -1438,7 +1453,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, } // Order by build_number - slices.SortFunc(history, func(a, b database.WorkspaceBuild) bool { + slices.SortFunc(history, func(a, b database.WorkspaceBuildThin) bool { // use greater than since we want descending order return a.BuildNumber > b.BuildNumber }) @@ -1477,7 +1492,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, if len(history) == 0 { return nil, sql.ErrNoRows } - return history, nil + return q.expandWorkspaceThins(history), nil } func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuild, error) { @@ -1495,7 +1510,7 @@ func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Con if workspaceBuild.BuildNumber != arg.BuildNumber { continue } - return workspaceBuild, nil + return q.expandWorkspaceThin(workspaceBuild), nil } return database.WorkspaceBuild{}, sql.ErrNoRows } @@ -1521,7 +1536,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsCreatedAfter(_ context.Context, after ti workspaceBuilds := make([]database.WorkspaceBuild, 0) for _, workspaceBuild := range q.workspaceBuilds { if workspaceBuild.CreatedAt.After(after) { - workspaceBuilds = append(workspaceBuilds, workspaceBuild) + workspaceBuilds = append(workspaceBuilds, q.expandWorkspaceThin(workspaceBuild)) } } return workspaceBuilds, nil @@ -3058,15 +3073,15 @@ func (q *fakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWork return workspace, nil } -func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) (database.WorkspaceBuild, error) { +func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) (database.WorkspaceBuildThin, error) { if err := validateDatabaseType(arg); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } q.mutex.Lock() defer q.mutex.Unlock() - workspaceBuild := database.WorkspaceBuild{ + workspaceBuild := database.WorkspaceBuildThin{ ID: arg.ID, CreatedAt: arg.CreatedAt, UpdatedAt: arg.UpdatedAt, @@ -3490,9 +3505,9 @@ func (q *fakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database. return sql.ErrNoRows } -func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.UpdateWorkspaceBuildByIDParams) (database.WorkspaceBuild, error) { +func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.UpdateWorkspaceBuildByIDParams) (database.WorkspaceBuildThin, error) { if err := validateDatabaseType(arg); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } q.mutex.Lock() @@ -3508,12 +3523,12 @@ func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.U q.workspaceBuilds[index] = workspaceBuild return workspaceBuild, nil } - return database.WorkspaceBuild{}, sql.ErrNoRows + return database.WorkspaceBuildThin{}, sql.ErrNoRows } -func (q *fakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuild, error) { +func (q *fakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuildThin, error) { if err := validateDatabaseType(arg); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } q.mutex.Lock() @@ -3527,7 +3542,7 @@ func (q *fakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg databa q.workspaceBuilds[index] = workspaceBuild return workspaceBuild, nil } - return database.WorkspaceBuild{}, sql.ErrNoRows + return database.WorkspaceBuildThin{}, sql.ErrNoRows } func (q *fakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database.UpdateWorkspaceDeletedByIDParams) error { @@ -4365,7 +4380,7 @@ func (q *fakeQuerier) GetQuotaConsumedForUser(_ context.Context, userID uuid.UUI continue } - var lastBuild database.WorkspaceBuild + var lastBuild database.WorkspaceBuildThin for _, build := range q.workspaceBuilds { if build.WorkspaceID != workspace.ID { continue diff --git a/coderd/database/dbgen/generator.go b/coderd/database/dbgen/generator.go index cb8f52c06529a..6abdc46e50df8 100644 --- a/coderd/database/dbgen/generator.go +++ b/coderd/database/dbgen/generator.go @@ -152,20 +152,31 @@ func Workspace(t testing.TB, db database.Store, orig database.Workspace) databas return workspace } -func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuild { +type workspaceBuild interface { + database.WorkspaceBuild | database.WorkspaceBuildThin +} + +func WorkspaceBuild[Build workspaceBuild](t testing.TB, db database.Store, orig Build) database.WorkspaceBuildThin { + var input database.WorkspaceBuildThin + switch any(orig).(type) { + case database.WorkspaceBuild: + input = any(orig).(database.WorkspaceBuild).ToThin() + case database.WorkspaceBuildThin: + input = any(orig).(database.WorkspaceBuildThin) + } build, err := db.InsertWorkspaceBuild(context.Background(), database.InsertWorkspaceBuildParams{ - ID: takeFirst(orig.ID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, database.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, database.Now()), - WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()), - TemplateVersionID: takeFirst(orig.TemplateVersionID, uuid.New()), - BuildNumber: takeFirst(orig.BuildNumber, 1), - Transition: takeFirst(orig.Transition, database.WorkspaceTransitionStart), - InitiatorID: takeFirst(orig.InitiatorID, uuid.New()), - JobID: takeFirst(orig.JobID, uuid.New()), - ProvisionerState: takeFirstSlice(orig.ProvisionerState, []byte{}), - Deadline: takeFirst(orig.Deadline, database.Now().Add(time.Hour)), - Reason: takeFirst(orig.Reason, database.BuildReasonInitiator), + ID: takeFirst(input.ID, uuid.New()), + CreatedAt: takeFirst(input.CreatedAt, database.Now()), + UpdatedAt: takeFirst(input.UpdatedAt, database.Now()), + WorkspaceID: takeFirst(input.WorkspaceID, uuid.New()), + TemplateVersionID: takeFirst(input.TemplateVersionID, uuid.New()), + BuildNumber: takeFirst(input.BuildNumber, 1), + Transition: takeFirst(input.Transition, database.WorkspaceTransitionStart), + InitiatorID: takeFirst(input.InitiatorID, uuid.New()), + JobID: takeFirst(input.JobID, uuid.New()), + ProvisionerState: takeFirstSlice(input.ProvisionerState, []byte{}), + Deadline: takeFirst(input.Deadline, database.Now().Add(time.Hour)), + Reason: takeFirst(input.Reason, database.BuildReasonInitiator), }) require.NoError(t, err, "insert workspace build") return build diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 44c598697ef8b..6aa9610998178 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -4,6 +4,8 @@ import ( "sort" "strconv" + "github.com/google/uuid" + "github.com/coder/coder/coderd/rbac" ) @@ -74,6 +76,12 @@ func (g Group) RBACObject() rbac.Object { InOrg(g.OrganizationID) } +func (b WorkspaceBuild) RBACObject() rbac.Object { + return rbac.ResourceWorkspace.WithID(b.WorkspaceID). + InOrg(b.OrganizationID). + WithOwner(b.WorkspaceOwnerID.String()) +} + func (w Workspace) RBACObject() rbac.Object { return rbac.ResourceWorkspace.WithID(w.ID). InOrg(w.OrganizationID). @@ -156,6 +164,48 @@ func (l License) RBACObject() rbac.Object { return rbac.ResourceLicense.WithIDString(strconv.FormatInt(int64(l.ID), 10)) } +func (b WorkspaceBuild) ToThin() WorkspaceBuildThin { + return WorkspaceBuildThin{ + ID: b.ID, + CreatedAt: b.CreatedAt, + UpdatedAt: b.UpdatedAt, + WorkspaceID: b.WorkspaceID, + TemplateVersionID: b.TemplateVersionID, + BuildNumber: b.BuildNumber, + Transition: b.Transition, + InitiatorID: b.InitiatorID, + ProvisionerState: b.ProvisionerState, + JobID: b.JobID, + Deadline: b.Deadline, + Reason: b.Reason, + DailyCost: b.DailyCost, + } +} + +func (b WorkspaceBuildThin) WithWorkspace(workspace Workspace) WorkspaceBuild { + return b.Expand(workspace.OrganizationID, workspace.OwnerID) +} + +func (b WorkspaceBuildThin) Expand(orgID, ownerID uuid.UUID) WorkspaceBuild { + return WorkspaceBuild{ + ID: b.ID, + CreatedAt: b.CreatedAt, + UpdatedAt: b.UpdatedAt, + WorkspaceID: b.WorkspaceID, + TemplateVersionID: b.TemplateVersionID, + BuildNumber: b.BuildNumber, + Transition: b.Transition, + InitiatorID: b.InitiatorID, + ProvisionerState: b.ProvisionerState, + JobID: b.JobID, + Deadline: b.Deadline, + Reason: b.Reason, + DailyCost: b.DailyCost, + OrganizationID: orgID, + WorkspaceOwnerID: ownerID, + } +} + func ConvertUserRows(rows []GetUsersRow) []User { users := make([]User, len(rows)) for i, r := range rows { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index cdbfa5921a1fe..db6a0fa219ddf 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -55,7 +55,7 @@ type sqlcQuerier interface { GetGroupsByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]Group, error) GetLastUpdateCheck(ctx context.Context) (string, error) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error) - GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuildThin, error) + GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error) GetLicenseByID(ctx context.Context, id int32) (License, error) GetLicenses(ctx context.Context) ([]License, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index b972c3d69bb06..c8aef05dbbb4c 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -5765,15 +5765,15 @@ func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg Ins const getLatestWorkspaceBuildByWorkspaceID = `-- name: GetLatestWorkspaceBuildByWorkspaceID :one SELECT - id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id + id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, organization_id, workspace_owner_id FROM - workspace_builds_rbac + workspace_builds_rbac WHERE - workspace_id = $1 + workspace_id = $1 ORDER BY build_number desc LIMIT - 1 + 1 ` func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error) { @@ -5800,7 +5800,7 @@ func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, w } const getLatestWorkspaceBuilds = `-- name: GetLatestWorkspaceBuilds :many -SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost +SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.organization_id, wb.workspace_owner_id FROM ( SELECT workspace_id, MAX(build_number) as max_build_number @@ -5810,19 +5810,19 @@ FROM ( workspace_id ) m JOIN - workspace_builds wb + workspace_builds_rbac wb ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number ` -func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuildThin, error) { +func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error) { rows, err := q.db.QueryContext(ctx, getLatestWorkspaceBuilds) if err != nil { return nil, err } defer rows.Close() - var items []WorkspaceBuildThin + var items []WorkspaceBuild for rows.Next() { - var i WorkspaceBuildThin + var i WorkspaceBuild if err := rows.Scan( &i.ID, &i.CreatedAt, @@ -5837,6 +5837,8 @@ func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceB &i.Deadline, &i.Reason, &i.DailyCost, + &i.OrganizationID, + &i.WorkspaceOwnerID, ); err != nil { return nil, err } @@ -5857,14 +5859,14 @@ FROM ( SELECT workspace_id, MAX(build_number) as max_build_number FROM - workspace_builds + workspace_builds WHERE workspace_id = ANY($1 :: uuid [ ]) GROUP BY workspace_id ) m JOIN - workspace_builds_rbac wb + workspace_builds_rbac wb ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number ` @@ -6019,9 +6021,9 @@ SELECT FROM workspace_builds_rbac WHERE - workspace_builds.workspace_id = $1 - AND workspace_builds.created_at > $2 - AND CASE + workspace_builds_rbac.workspace_id = $1 + AND workspace_builds_rbac.created_at > $2 + AND CASE -- This allows using the last element on a page as effectively a cursor. -- This is an important option for scripts that need to paginate without -- duplicating or missing data. diff --git a/coderd/database/queries/workspacebuilds.sql b/coderd/database/queries/workspacebuilds.sql index 6466093534dbe..da1a9d5356a22 100644 --- a/coderd/database/queries/workspacebuilds.sql +++ b/coderd/database/queries/workspacebuilds.sql @@ -36,8 +36,8 @@ SELECT FROM workspace_builds_rbac WHERE - workspace_builds.workspace_id = $1 - AND workspace_builds.created_at > @since + workspace_builds_rbac.workspace_id = $1 + AND workspace_builds_rbac.created_at > @since AND CASE -- This allows using the last element on a page as effectively a cursor. -- This is an important option for scripts that need to paginate without @@ -102,7 +102,7 @@ FROM ( workspace_id ) m JOIN - workspace_builds wb + workspace_builds_rbac wb ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number; -- name: InsertWorkspaceBuild :one diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 26176f5a0c93e..32195f60c238b 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -584,7 +584,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("insert provisioner job: %w", err) } - workspaceBuild, err = db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{ + thinBuild, err := db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{ ID: workspaceBuildID, CreatedAt: database.Now(), UpdatedAt: database.Now(), @@ -601,6 +601,9 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("insert workspace build: %w", err) } + // Assign owning fields. + workspaceBuild = thinBuild.WithWorkspace(workspace) + names := make([]string, 0, len(parameters)) values := make([]string, 0, len(parameters)) for _, param := range parameters { diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 5e7398da29f23..f138066eb6d74 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -528,7 +528,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req if err != nil { return xerrors.Errorf("insert provisioner job: %w", err) } - workspaceBuild, err = db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{ + workspaceBuildThin, err := db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{ ID: workspaceBuildID, CreatedAt: now, UpdatedAt: now, @@ -544,6 +544,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req if err != nil { return xerrors.Errorf("insert workspace build: %w", err) } + workspaceBuild = workspaceBuildThin.WithWorkspace(workspace) names := make([]string, 0, len(createWorkspace.RichParameterValues)) values := make([]string, 0, len(createWorkspace.RichParameterValues)) From 0b98766227664bf2f57e36f46057599142f3161b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 11:07:30 -0600 Subject: [PATCH 03/10] Remove uneeded extra code --- coderd/database/dbgen/generator.go | 37 +++++++++++------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/coderd/database/dbgen/generator.go b/coderd/database/dbgen/generator.go index 6abdc46e50df8..0ec2bcb025eba 100644 --- a/coderd/database/dbgen/generator.go +++ b/coderd/database/dbgen/generator.go @@ -152,31 +152,20 @@ func Workspace(t testing.TB, db database.Store, orig database.Workspace) databas return workspace } -type workspaceBuild interface { - database.WorkspaceBuild | database.WorkspaceBuildThin -} - -func WorkspaceBuild[Build workspaceBuild](t testing.TB, db database.Store, orig Build) database.WorkspaceBuildThin { - var input database.WorkspaceBuildThin - switch any(orig).(type) { - case database.WorkspaceBuild: - input = any(orig).(database.WorkspaceBuild).ToThin() - case database.WorkspaceBuildThin: - input = any(orig).(database.WorkspaceBuildThin) - } +func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuildThin { build, err := db.InsertWorkspaceBuild(context.Background(), database.InsertWorkspaceBuildParams{ - ID: takeFirst(input.ID, uuid.New()), - CreatedAt: takeFirst(input.CreatedAt, database.Now()), - UpdatedAt: takeFirst(input.UpdatedAt, database.Now()), - WorkspaceID: takeFirst(input.WorkspaceID, uuid.New()), - TemplateVersionID: takeFirst(input.TemplateVersionID, uuid.New()), - BuildNumber: takeFirst(input.BuildNumber, 1), - Transition: takeFirst(input.Transition, database.WorkspaceTransitionStart), - InitiatorID: takeFirst(input.InitiatorID, uuid.New()), - JobID: takeFirst(input.JobID, uuid.New()), - ProvisionerState: takeFirstSlice(input.ProvisionerState, []byte{}), - Deadline: takeFirst(input.Deadline, database.Now().Add(time.Hour)), - Reason: takeFirst(input.Reason, database.BuildReasonInitiator), + ID: takeFirst(orig.ID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, database.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, database.Now()), + WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()), + TemplateVersionID: takeFirst(orig.TemplateVersionID, uuid.New()), + BuildNumber: takeFirst(orig.BuildNumber, 1), + Transition: takeFirst(orig.Transition, database.WorkspaceTransitionStart), + InitiatorID: takeFirst(orig.InitiatorID, uuid.New()), + JobID: takeFirst(orig.JobID, uuid.New()), + ProvisionerState: takeFirstSlice(orig.ProvisionerState, []byte{}), + Deadline: takeFirst(orig.Deadline, database.Now().Add(time.Hour)), + Reason: takeFirst(orig.Reason, database.BuildReasonInitiator), }) require.NoError(t, err, "insert workspace build") return build From 8a269470f7acc5cc261443192a914d341de43600 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 11:17:53 -0600 Subject: [PATCH 04/10] UsePostFilter --- coderd/database/dbauthz/querier.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/coderd/database/dbauthz/querier.go b/coderd/database/dbauthz/querier.go index d1ba3604f7ee5..771a8583f3b62 100644 --- a/coderd/database/dbauthz/querier.go +++ b/coderd/database/dbauthz/querier.go @@ -1143,17 +1143,7 @@ func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, work } func (q *querier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceBuild, error) { - // This is not ideal as not all builds will be returned if the workspace cannot be read. - // This should probably be handled differently? Maybe join workspace builds with workspace - // ownership properties and filter on that. - for _, id := range ids { - _, err := q.GetWorkspaceByID(ctx, id) - if err != nil { - return nil, err - } - } - - return q.db.GetLatestWorkspaceBuildsByWorkspaceIDs(ctx, ids) + return fetchWithPostFilter(q.auth, q.db.GetLatestWorkspaceBuildsByWorkspaceIDs)(ctx, ids) } func (q *querier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (database.WorkspaceAgent, error) { @@ -1255,10 +1245,19 @@ func (q *querier) GetWorkspaceBuildParameters(ctx context.Context, workspaceBuil } func (q *querier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg database.GetWorkspaceBuildsByWorkspaceIDParams) ([]database.WorkspaceBuild, error) { - if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil { + builds, err := q.db.GetWorkspaceBuildsByWorkspaceID(ctx, arg) + if err != nil { + return nil, err + } + if len(builds) == 0 { + return nil, sql.ErrNoRows + } + // All builds come from the same workspace, so we only need to check the first one. + err = q.authorizeContext(ctx, rbac.ActionRead, builds[0]) + if err != nil { return nil, err } - return q.db.GetWorkspaceBuildsByWorkspaceID(ctx, arg) + return builds, nil } func (q *querier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUID) (database.Workspace, error) { From 6bee9af396d02144aea9b0cf21ccfbd370815a46 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 11:25:21 -0600 Subject: [PATCH 05/10] Remove some db round trips --- coderd/database/dbauthz/querier.go | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/coderd/database/dbauthz/querier.go b/coderd/database/dbauthz/querier.go index 771a8583f3b62..66653de19be11 100644 --- a/coderd/database/dbauthz/querier.go +++ b/coderd/database/dbauthz/querier.go @@ -1317,11 +1317,7 @@ func (q *querier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.U if err != nil { return nil, err } - workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID) - if err != nil { - return nil, err - } - obj = workspace + obj = build default: return nil, xerrors.Errorf("unknown job type: %s", job.Type) } @@ -1362,12 +1358,7 @@ func (q *querier) InsertWorkspaceBuildParameters(ctx context.Context, arg databa return err } - workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID) - if err != nil { - return err - } - - err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace) + err = q.authorizeContext(ctx, rbac.ActionUpdate, build) if err != nil { return err } @@ -1431,11 +1422,7 @@ func (q *querier) UpdateWorkspaceBuildByID(ctx context.Context, arg database.Upd return database.WorkspaceBuildThin{}, err } - workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID) - if err != nil { - return database.WorkspaceBuildThin{}, err - } - err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace.RBACObject()) + err = q.authorizeContext(ctx, rbac.ActionUpdate, build) if err != nil { return database.WorkspaceBuildThin{}, err } From 81165d535e8ba434dba0790fb0abebd4f5daecf6 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 11:27:49 -0600 Subject: [PATCH 06/10] Fix dbgen unit test --- coderd/database/dbgen/generator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/database/dbgen/generator_test.go b/coderd/database/dbgen/generator_test.go index c09cc6df8a466..6082d1d33addb 100644 --- a/coderd/database/dbgen/generator_test.go +++ b/coderd/database/dbgen/generator_test.go @@ -167,7 +167,7 @@ func TestGenerator(t *testing.T) { t.Parallel() db := dbfake.New() exp := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{}) - require.Equal(t, exp, must(db.GetWorkspaceBuildByID(context.Background(), exp.ID))) + require.Equal(t, exp, must(db.GetWorkspaceBuildByID(context.Background(), exp.ID)).ToThin()) }) t.Run("User", func(t *testing.T) { From e252c0420a5ff7adac7f4234534b739430efffae Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 11:41:55 -0600 Subject: [PATCH 07/10] Fix compile issue --- coderd/provisionerdserver/provisionerdserver_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index bcc0d6b0ad93a..34ee3403fbdf7 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -635,13 +635,14 @@ func TestFailJob(t *testing.T) { ID: uuid.New(), }) require.NoError(t, err) - build, err := srv.Database.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{ + buildThin, err := srv.Database.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{ ID: uuid.New(), WorkspaceID: workspace.ID, Transition: database.WorkspaceTransitionStart, Reason: database.BuildReasonInitiator, }) require.NoError(t, err) + build := buildThin.WithWorkspace(workspace) input, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{ WorkspaceBuildID: build.ID, }) From b698672e83458a57433506d9e44e63036367f484 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 15:28:35 -0600 Subject: [PATCH 08/10] fix merge --- coderd/database/dbfake/databasefake.go | 58 ++++++++++++++++---------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go index 8823d8d9a8174..27dca53d5b9dc 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -40,7 +40,6 @@ func New() database.Store { mutex: &sync.RWMutex{}, data: &data{ apiKeys: make([]database.APIKey, 0), - agentStats: make([]database.AgentStat, 0), organizationMembers: make([]database.OrganizationMember, 0), organizations: make([]database.Organization, 0), users: make([]database.User, 0), @@ -1214,7 +1213,7 @@ func (q *fakeQuerier) GetWorkspaceByAgentID(_ context.Context, agentID uuid.UUID return database.Workspace{}, sql.ErrNoRows } - var build database.WorkspaceBuild + var build database.WorkspaceBuildThin for _, _build := range q.workspaceBuilds { if _build.JobID == resource.JobID { build = _build @@ -1334,7 +1333,7 @@ func (q *fakeQuerier) GetWorkspaceBuildByID(_ context.Context, id uuid.UUID) (da for _, history := range q.workspaceBuilds { if history.ID == id { - return history, nil + return q.expandWorkspaceThin(history), nil } } return database.WorkspaceBuild{}, sql.ErrNoRows @@ -1346,7 +1345,7 @@ func (q *fakeQuerier) GetWorkspaceBuildByJobID(_ context.Context, jobID uuid.UUI for _, build := range q.workspaceBuilds { if build.JobID == jobID { - return build, nil + return q.expandWorkspaceThin(build), nil } } return database.WorkspaceBuild{}, sql.ErrNoRows @@ -1360,7 +1359,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(_ context.Context, wo var buildNum int32 = -1 for _, workspaceBuild := range q.workspaceBuilds { if workspaceBuild.WorkspaceID == workspaceID && workspaceBuild.BuildNumber > buildNum { - row = workspaceBuild + row = q.expandWorkspaceThin(workspaceBuild) buildNum = workspaceBuild.BuildNumber } } @@ -1379,7 +1378,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.Wo for _, workspaceBuild := range q.workspaceBuilds { id := workspaceBuild.WorkspaceID if workspaceBuild.BuildNumber > buildNumbers[id] { - builds[id] = workspaceBuild + builds[id] = q.expandWorkspaceThin(workspaceBuild) buildNumbers[id] = workspaceBuild.BuildNumber } } @@ -1405,7 +1404,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context, for _, workspaceBuild := range q.workspaceBuilds { for _, id := range ids { if id == workspaceBuild.WorkspaceID && workspaceBuild.BuildNumber > buildNumbers[id] { - builds[id] = workspaceBuild + builds[id] = q.expandWorkspaceThin(workspaceBuild) buildNumbers[id] = workspaceBuild.BuildNumber } } @@ -1433,7 +1432,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, q.mutex.RLock() defer q.mutex.RUnlock() - history := make([]database.WorkspaceBuild, 0) + history := make([]database.WorkspaceBuildThin, 0) for _, workspaceBuild := range q.workspaceBuilds { if workspaceBuild.CreatedAt.Before(params.Since) { continue @@ -1444,7 +1443,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, } // Order by build_number - slices.SortFunc(history, func(a, b database.WorkspaceBuild) bool { + slices.SortFunc(history, func(a, b database.WorkspaceBuildThin) bool { // use greater than since we want descending order return a.BuildNumber > b.BuildNumber }) @@ -1483,7 +1482,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, if len(history) == 0 { return nil, sql.ErrNoRows } - return history, nil + return q.expandWorkspaceThins(history), nil } func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuild, error) { @@ -1501,7 +1500,7 @@ func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Con if workspaceBuild.BuildNumber != arg.BuildNumber { continue } - return workspaceBuild, nil + return q.expandWorkspaceThin(workspaceBuild), nil } return database.WorkspaceBuild{}, sql.ErrNoRows } @@ -1527,7 +1526,7 @@ func (q *fakeQuerier) GetWorkspaceBuildsCreatedAfter(_ context.Context, after ti workspaceBuilds := make([]database.WorkspaceBuild, 0) for _, workspaceBuild := range q.workspaceBuilds { if workspaceBuild.CreatedAt.After(after) { - workspaceBuilds = append(workspaceBuilds, workspaceBuild) + workspaceBuilds = append(workspaceBuilds, q.expandWorkspaceThin(workspaceBuild)) } } return workspaceBuilds, nil @@ -3064,15 +3063,15 @@ func (q *fakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWork return workspace, nil } -func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) (database.WorkspaceBuild, error) { +func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) (database.WorkspaceBuildThin, error) { if err := validateDatabaseType(arg); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } q.mutex.Lock() defer q.mutex.Unlock() - workspaceBuild := database.WorkspaceBuild{ + workspaceBuild := database.WorkspaceBuildThin{ ID: arg.ID, CreatedAt: arg.CreatedAt, UpdatedAt: arg.UpdatedAt, @@ -3496,9 +3495,9 @@ func (q *fakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database. return sql.ErrNoRows } -func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.UpdateWorkspaceBuildByIDParams) (database.WorkspaceBuild, error) { +func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.UpdateWorkspaceBuildByIDParams) (database.WorkspaceBuildThin, error) { if err := validateDatabaseType(arg); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } q.mutex.Lock() @@ -3514,12 +3513,12 @@ func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.U q.workspaceBuilds[index] = workspaceBuild return workspaceBuild, nil } - return database.WorkspaceBuild{}, sql.ErrNoRows + return database.WorkspaceBuildThin{}, sql.ErrNoRows } -func (q *fakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuild, error) { +func (q *fakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) (database.WorkspaceBuildThin, error) { if err := validateDatabaseType(arg); err != nil { - return database.WorkspaceBuild{}, err + return database.WorkspaceBuildThin{}, err } q.mutex.Lock() @@ -3533,7 +3532,7 @@ func (q *fakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg databa q.workspaceBuilds[index] = workspaceBuild return workspaceBuild, nil } - return database.WorkspaceBuild{}, sql.ErrNoRows + return database.WorkspaceBuildThin{}, sql.ErrNoRows } func (q *fakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database.UpdateWorkspaceDeletedByIDParams) error { @@ -4377,7 +4376,7 @@ func (q *fakeQuerier) GetQuotaConsumedForUser(_ context.Context, userID uuid.UUI continue } if build.CreatedAt.After(lastBuild.CreatedAt) { - lastBuild = build + lastBuild = q.expandWorkspaceThin(build) } } sum += int64(lastBuild.DailyCost) @@ -4401,3 +4400,18 @@ func (q *fakeQuerier) UpdateWorkspaceAgentLifecycleStateByID(_ context.Context, } return sql.ErrNoRows } + +// expandWorkspaceThins must be called from a locked context. +func (q *fakeQuerier) expandWorkspaceThins(thins []database.WorkspaceBuildThin) []database.WorkspaceBuild { + cpy := make([]database.WorkspaceBuild, 0, len(thins)) + for _, thin := range thins { + cpy = append(cpy, q.expandWorkspaceThin(thin)) + } + return cpy +} + +// expandWorkspaceThin must be called from a locked context. +func (q *fakeQuerier) expandWorkspaceThin(thin database.WorkspaceBuildThin) database.WorkspaceBuild { + w, _ := q.GetWorkspaceByID(context.Background(), thin.WorkspaceID) + return thin.Expand(w.OrganizationID, w.OwnerID) +} From 3ce72c513695f91d3e0e12469d8164fbf5d4213e Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Feb 2023 16:27:58 -0600 Subject: [PATCH 09/10] rename migration file +1 --- ...e_build_view.down.sql => 000102_workspace_build_view.down.sql} | 0 ...space_build_view.up.sql => 000102_workspace_build_view.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000101_workspace_build_view.down.sql => 000102_workspace_build_view.down.sql} (100%) rename coderd/database/migrations/{000101_workspace_build_view.up.sql => 000102_workspace_build_view.up.sql} (100%) diff --git a/coderd/database/migrations/000101_workspace_build_view.down.sql b/coderd/database/migrations/000102_workspace_build_view.down.sql similarity index 100% rename from coderd/database/migrations/000101_workspace_build_view.down.sql rename to coderd/database/migrations/000102_workspace_build_view.down.sql diff --git a/coderd/database/migrations/000101_workspace_build_view.up.sql b/coderd/database/migrations/000102_workspace_build_view.up.sql similarity index 100% rename from coderd/database/migrations/000101_workspace_build_view.up.sql rename to coderd/database/migrations/000102_workspace_build_view.up.sql From c3218f71cab8d571c2a95bca08efe2816a41fa86 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 1 Mar 2023 16:59:53 -0600 Subject: [PATCH 10/10] fix fake --- coderd/database/dbfake/databasefake.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go index 27dca53d5b9dc..93ed5d0f73746 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -4370,13 +4370,13 @@ func (q *fakeQuerier) GetQuotaConsumedForUser(_ context.Context, userID uuid.UUI continue } - var lastBuild database.WorkspaceBuild + var lastBuild database.WorkspaceBuildThin for _, build := range q.workspaceBuilds { if build.WorkspaceID != workspace.ID { continue } if build.CreatedAt.After(lastBuild.CreatedAt) { - lastBuild = q.expandWorkspaceThin(build) + lastBuild = build } } sum += int64(lastBuild.DailyCost)