Revert "chore: Implement joins with golang templates (#6429)" (#6560)

This reverts commit 8b125d6c5d.
This commit is contained in:
Kyle Carberry 2023-03-10 10:39:02 -06:00 committed by GitHub
parent a8433b18e4
commit 7eb2c2ff6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 660 additions and 894 deletions

View File

@ -14,7 +14,7 @@ type Auditable interface {
database.User |
database.Workspace |
database.GitSSHKey |
database.WorkspaceBuildRBAC |
database.WorkspaceBuild |
database.AuditableGroup |
database.License
}

View File

@ -62,7 +62,7 @@ func ResourceTarget[T Auditable](tgt T) string {
return typed.Username
case database.Workspace:
return typed.Name
case database.WorkspaceBuildRBAC:
case database.WorkspaceBuild:
// this isn't used
return ""
case database.GitSSHKey:
@ -89,7 +89,7 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
return typed.ID
case database.Workspace:
return typed.ID
case database.WorkspaceBuildRBAC:
case database.WorkspaceBuild:
return typed.ID
case database.GitSSHKey:
return typed.UserID
@ -114,7 +114,7 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
return database.ResourceTypeUser
case database.Workspace:
return database.ResourceTypeWorkspace
case database.WorkspaceBuildRBAC:
case database.WorkspaceBuild:
return database.ResourceTypeWorkspaceBuild
case database.GitSSHKey:
return database.ResourceTypeGitSshKey

View File

@ -204,7 +204,7 @@ func isEligibleForAutoStartStop(ws database.Workspace) bool {
func getNextTransition(
ws database.Workspace,
priorHistory database.WorkspaceBuildRBAC,
priorHistory database.WorkspaceBuild,
priorJob database.ProvisionerJob,
) (
validTransition database.WorkspaceTransition,
@ -239,7 +239,7 @@ func getNextTransition(
// TODO(cian): this function duplicates most of api.postWorkspaceBuilds. Refactor.
// See: https://github.com/coder/coder/issues/1401
func build(ctx context.Context, store database.Store, workspace database.Workspace, trans database.WorkspaceTransition, priorHistory database.WorkspaceBuildRBAC, priorJob database.ProvisionerJob) error {
func build(ctx context.Context, store database.Store, workspace database.Workspace, trans database.WorkspaceTransition, priorHistory database.WorkspaceBuild, priorJob database.ProvisionerJob) error {
template, err := store.GetTemplateByID(ctx, workspace.TemplateID)
if err != nil {
return xerrors.Errorf("get workspace template: %w", err)

View File

@ -2,16 +2,17 @@ package executor_test
import (
"context"
"os"
"testing"
"time"
"github.com/google/uuid"
"go.uber.org/goleak"
"github.com/google/uuid"
"github.com/coder/coder/coderd/autobuild/executor"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbtestutil"
"github.com/coder/coder/coderd/schedule"
"github.com/coder/coder/coderd/util/ptr"
"github.com/coder/coder/codersdk"
@ -492,7 +493,7 @@ func TestExecutorWorkspaceAutostopNoWaitChangedMyMind(t *testing.T) {
}
func TestExecutorAutostartMultipleOK(t *testing.T) {
if !dbtestutil.UsingRealDatabase() {
if os.Getenv("DB") == "" {
t.Skip(`This test only really works when using a "real" database, similar to a HA setup`)
}

View File

@ -16,8 +16,6 @@ import (
"github.com/jmoiron/sqlx"
"golang.org/x/xerrors"
"github.com/coder/coder/coderd/database/sqlxqueries"
)
// Store contains all queryable database functions.
@ -39,21 +37,11 @@ type DBTX interface {
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
// Extends the sqlx interface
sqlx.QueryerContext
}
// New creates a new database store using a SQL database connection.
func New(sdb *sql.DB) Store {
dbx := sqlx.NewDb(sdb, "postgres")
// Load the embedded queries. If this fails, some of our queries
// will never work. This is a fatal developer error that should never
// happen.
_, err := sqlxqueries.LoadQueries()
if err != nil {
panic(xerrors.Errorf("load queries: %w", err))
}
return &sqlQuerier{
db: dbx,
sdb: dbx,

View File

@ -1167,12 +1167,25 @@ func (q *querier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesP
return q.db.GetAuthorizedWorkspaces(ctx, arg, prep)
}
func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuildRBAC, error) {
return fetch(q.log, q.auth, q.db.GetLatestWorkspaceBuildByWorkspaceID)(ctx, workspaceID)
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)
}
func (q *querier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceBuildRBAC, error) {
return fetchWithPostFilter(q.auth, q.db.GetLatestWorkspaceBuildsByWorkspaceIDs)(ctx, ids)
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)
}
func (q *querier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (database.WorkspaceAgent, error) {
@ -1250,16 +1263,35 @@ func (q *querier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UU
return q.db.GetWorkspaceAppsByAgentID(ctx, agentID)
}
func (q *querier) GetWorkspaceBuildByID(ctx context.Context, buildID uuid.UUID) (database.WorkspaceBuildRBAC, error) {
return fetch(q.log, q.auth, q.db.GetWorkspaceBuildByID)(ctx, buildID)
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
}
func (q *querier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (database.WorkspaceBuildRBAC, error) {
return fetch(q.log, q.auth, q.db.GetWorkspaceBuildByJobID)(ctx, jobID)
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
}
func (q *querier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuildRBAC, error) {
return fetch(q.log, q.auth, q.db.GetWorkspaceBuildByWorkspaceIDAndBuildNumber)(ctx, arg)
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)
}
func (q *querier) GetWorkspaceBuildParameters(ctx context.Context, workspaceBuildID uuid.UUID) ([]database.WorkspaceBuildParameter, error) {
@ -1273,20 +1305,11 @@ func (q *querier) GetWorkspaceBuildParameters(ctx context.Context, workspaceBuil
return q.db.GetWorkspaceBuildParameters(ctx, workspaceBuildID)
}
func (q *querier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg database.GetWorkspaceBuildsByWorkspaceIDParams) ([]database.WorkspaceBuildRBAC, error) {
builds, err := q.db.GetWorkspaceBuildsByWorkspaceID(ctx, arg)
if err != nil {
func (q *querier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg database.GetWorkspaceBuildsByWorkspaceIDParams) ([]database.WorkspaceBuild, error) {
if _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID); err != nil {
return nil, err
}
if len(builds) == 0 {
return []database.WorkspaceBuildRBAC{}, nil
}
// 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 builds, nil
return q.db.GetWorkspaceBuildsByWorkspaceID(ctx, arg)
}
func (q *querier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUID) (database.Workspace, error) {
@ -1346,7 +1369,11 @@ func (q *querier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.U
if err != nil {
return nil, err
}
obj = build
workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID)
if err != nil {
return nil, err
}
obj = workspace
default:
return nil, xerrors.Errorf("unknown job type: %s", job.Type)
}
@ -1387,7 +1414,12 @@ func (q *querier) InsertWorkspaceBuildParameters(ctx context.Context, arg databa
return err
}
err = q.authorizeContext(ctx, rbac.ActionUpdate, build)
workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID)
if err != nil {
return err
}
err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace)
if err != nil {
return err
}
@ -1451,7 +1483,11 @@ func (q *querier) UpdateWorkspaceBuildByID(ctx context.Context, arg database.Upd
return database.WorkspaceBuild{}, err
}
err = q.authorizeContext(ctx, rbac.ActionUpdate, build)
workspace, err := q.db.GetWorkspaceByID(ctx, build.WorkspaceID)
if err != nil {
return database.WorkspaceBuild{}, err
}
err = q.authorizeContext(ctx, rbac.ActionUpdate, workspace.RBACObject())
if err != nil {
return database.WorkspaceBuild{}, err
}

View File

@ -956,16 +956,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.WithWorkspace(ws))
check.Args(ws.ID).Asserts(ws, rbac.ActionRead).Returns(b)
}))
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}).WithWorkspace(ws)
b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID})
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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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)
@ -979,7 +979,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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*/ ).
@ -987,7 +987,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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{
@ -997,7 +997,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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{
@ -1006,7 +1006,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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})
@ -1018,7 +1018,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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})
@ -1028,13 +1028,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()}).WithWorkspace(aWs)
aBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: aWs.ID, JobID: uuid.New()})
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()}).WithWorkspace(bWs)
bBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: bWs.ID, JobID: uuid.New()})
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})
@ -1045,17 +1045,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}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID})
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}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID})
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}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, BuildNumber: 10})
check.Args(database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams{
WorkspaceID: ws.ID,
BuildNumber: build.BuildNumber,
@ -1069,14 +1069,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}).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)
_ = 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})
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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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)
@ -1091,14 +1091,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
_ = 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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
_ = 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})
@ -1107,7 +1107,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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{})
}))
@ -1123,7 +1123,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()}).WithWorkspace(ws)
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
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*/ ).
@ -1207,11 +1207,9 @@ func (s *MethodTestSuite) TestWorkspace() {
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
check.Args(database.UpdateWorkspaceBuildByIDParams{
ID: build.ID,
UpdatedAt: build.UpdatedAt,
Deadline: build.Deadline,
ProvisionerState: build.ProvisionerState,
MaxDeadline: build.MaxDeadline,
ID: build.ID,
UpdatedAt: build.UpdatedAt,
Deadline: build.Deadline,
}).Asserts(ws, rbac.ActionUpdate).Returns(build)
}))
s.Run("SoftDeleteWorkspaceByID", s.Subtest(func(db database.Store, check *expects) {

View File

@ -76,7 +76,7 @@ func (q *querier) GetUserLinkByUserIDLoginType(ctx context.Context, arg database
return q.db.GetUserLinkByUserIDLoginType(ctx, arg)
}
func (q *querier) GetLatestWorkspaceBuilds(ctx context.Context) ([]database.WorkspaceBuildRBAC, error) {
func (q *querier) GetLatestWorkspaceBuilds(ctx context.Context) ([]database.WorkspaceBuild, error) {
// This function is a system function until we implement a join for workspace builds.
// This is because we need to query for all related workspaces to the returned builds.
// This is a very inefficient method of fetching the latest workspace builds.
@ -177,7 +177,7 @@ func (q *querier) GetLastUpdateCheck(ctx context.Context) (string, error) {
// Telemetry related functions. These functions are system functions for returning
// telemetry data. Never called by a user.
func (q *querier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceBuildRBAC, error) {
func (q *querier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceBuild, error) {
return q.db.GetWorkspaceBuildsCreatedAfter(ctx, createdAt)
}

View File

@ -34,8 +34,8 @@ func (s *MethodTestSuite) TestSystemFunctions() {
}).Asserts().Returns(l)
}))
s.Run("GetLatestWorkspaceBuilds", s.Subtest(func(db database.Store, check *expects) {
dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuildRBAC{})
dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuildRBAC{})
dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{})
dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{})
check.Args().Asserts()
}))
s.Run("GetWorkspaceAgentByAuthToken", s.Subtest(func(db database.Store, check *expects) {
@ -92,7 +92,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
check.Args().Asserts()
}))
s.Run("UpdateWorkspaceBuildCostByID", s.Subtest(func(db database.Store, check *expects) {
b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuildRBAC{})
b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{})
o := b
o.DailyCost = 10
check.Args(database.UpdateWorkspaceBuildCostByIDParams{

View File

@ -1452,66 +1452,66 @@ func (q *fakeQuerier) GetWorkspaceAppsByAgentIDs(_ context.Context, ids []uuid.U
return apps, nil
}
func (q *fakeQuerier) GetWorkspaceBuildByID(_ context.Context, id uuid.UUID) (database.WorkspaceBuildRBAC, error) {
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 q.expandWorkspaceThin(history), nil
return history, nil
}
}
return database.WorkspaceBuildRBAC{}, sql.ErrNoRows
return database.WorkspaceBuild{}, sql.ErrNoRows
}
func (q *fakeQuerier) GetWorkspaceBuildByJobID(_ context.Context, jobID uuid.UUID) (database.WorkspaceBuildRBAC, error) {
func (q *fakeQuerier) GetWorkspaceBuildByJobID(_ context.Context, jobID uuid.UUID) (database.WorkspaceBuild, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
for _, build := range q.workspaceBuilds {
if build.JobID == jobID {
return q.expandWorkspaceThin(build), nil
return build, nil
}
}
return database.WorkspaceBuildRBAC{}, sql.ErrNoRows
return database.WorkspaceBuild{}, sql.ErrNoRows
}
func (q *fakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuildRBAC, error) {
func (q *fakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
return q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspaceID)
}
func (q *fakeQuerier) getLatestWorkspaceBuildByWorkspaceIDNoLock(_ context.Context, workspaceID uuid.UUID) (database.WorkspaceBuildRBAC, error) {
var row database.WorkspaceBuildRBAC
func (q *fakeQuerier) getLatestWorkspaceBuildByWorkspaceIDNoLock(_ context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
var row database.WorkspaceBuild
var buildNum int32 = -1
for _, workspaceBuild := range q.workspaceBuilds {
if workspaceBuild.WorkspaceID == workspaceID && workspaceBuild.BuildNumber > buildNum {
row = q.expandWorkspaceThin(workspaceBuild)
row = workspaceBuild
buildNum = workspaceBuild.BuildNumber
}
}
if buildNum == -1 {
return database.WorkspaceBuildRBAC{}, sql.ErrNoRows
return database.WorkspaceBuild{}, sql.ErrNoRows
}
return row, nil
}
func (q *fakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.WorkspaceBuildRBAC, error) {
func (q *fakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.WorkspaceBuild, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
builds := make(map[uuid.UUID]database.WorkspaceBuildRBAC)
builds := make(map[uuid.UUID]database.WorkspaceBuild)
buildNumbers := make(map[uuid.UUID]int32)
for _, workspaceBuild := range q.workspaceBuilds {
id := workspaceBuild.WorkspaceID
if workspaceBuild.BuildNumber > buildNumbers[id] {
builds[id] = q.expandWorkspaceThin(workspaceBuild)
builds[id] = workspaceBuild
buildNumbers[id] = workspaceBuild.BuildNumber
}
}
var returnBuilds []database.WorkspaceBuildRBAC
var returnBuilds []database.WorkspaceBuild
for i, n := range buildNumbers {
if n > 0 {
b := builds[i]
@ -1524,21 +1524,21 @@ func (q *fakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.Wo
return returnBuilds, nil
}
func (q *fakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceBuildRBAC, error) {
func (q *fakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceBuild, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
builds := make(map[uuid.UUID]database.WorkspaceBuildRBAC)
builds := make(map[uuid.UUID]database.WorkspaceBuild)
buildNumbers := make(map[uuid.UUID]int32)
for _, workspaceBuild := range q.workspaceBuilds {
for _, id := range ids {
if id == workspaceBuild.WorkspaceID && workspaceBuild.BuildNumber > buildNumbers[id] {
builds[id] = q.expandWorkspaceThin(workspaceBuild)
builds[id] = workspaceBuild
buildNumbers[id] = workspaceBuild.BuildNumber
}
}
}
var returnBuilds []database.WorkspaceBuildRBAC
var returnBuilds []database.WorkspaceBuild
for i, n := range buildNumbers {
if n > 0 {
b := builds[i]
@ -1553,7 +1553,7 @@ func (q *fakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context,
func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context,
params database.GetWorkspaceBuildsByWorkspaceIDParams,
) ([]database.WorkspaceBuildRBAC, error) {
) ([]database.WorkspaceBuild, error) {
if err := validateDatabaseType(params); err != nil {
return nil, err
}
@ -1561,18 +1561,18 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context,
q.mutex.RLock()
defer q.mutex.RUnlock()
history := make([]database.WorkspaceBuildRBAC, 0)
history := make([]database.WorkspaceBuild, 0)
for _, workspaceBuild := range q.workspaceBuilds {
if workspaceBuild.CreatedAt.Before(params.Since) {
continue
}
if workspaceBuild.WorkspaceID == params.WorkspaceID {
history = append(history, q.expandWorkspaceThin(workspaceBuild))
history = append(history, workspaceBuild)
}
}
// Order by build_number
slices.SortFunc(history, func(a, b database.WorkspaceBuildRBAC) bool {
slices.SortFunc(history, func(a, b database.WorkspaceBuild) bool {
// use greater than since we want descending order
return a.BuildNumber > b.BuildNumber
})
@ -1614,9 +1614,9 @@ func (q *fakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context,
return history, nil
}
func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuildRBAC, error) {
func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuild, error) {
if err := validateDatabaseType(arg); err != nil {
return database.WorkspaceBuildRBAC{}, err
return database.WorkspaceBuild{}, err
}
q.mutex.RLock()
@ -1629,9 +1629,9 @@ func (q *fakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Con
if workspaceBuild.BuildNumber != arg.BuildNumber {
continue
}
return q.expandWorkspaceThin(workspaceBuild), nil
return workspaceBuild, nil
}
return database.WorkspaceBuildRBAC{}, sql.ErrNoRows
return database.WorkspaceBuild{}, sql.ErrNoRows
}
func (q *fakeQuerier) GetWorkspaceBuildParameters(_ context.Context, workspaceBuildID uuid.UUID) ([]database.WorkspaceBuildParameter, error) {
@ -1648,14 +1648,14 @@ func (q *fakeQuerier) GetWorkspaceBuildParameters(_ context.Context, workspaceBu
return params, nil
}
func (q *fakeQuerier) GetWorkspaceBuildsCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceBuildRBAC, error) {
func (q *fakeQuerier) GetWorkspaceBuildsCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceBuild, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
workspaceBuilds := make([]database.WorkspaceBuildRBAC, 0)
workspaceBuilds := make([]database.WorkspaceBuild, 0)
for _, workspaceBuild := range q.workspaceBuilds {
if workspaceBuild.CreatedAt.After(after) {
workspaceBuilds = append(workspaceBuilds, q.expandWorkspaceThin(workspaceBuild))
workspaceBuilds = append(workspaceBuilds, workspaceBuild)
}
}
return workspaceBuilds, nil
@ -3250,7 +3250,6 @@ func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.Inser
JobID: arg.JobID,
ProvisionerState: arg.ProvisionerState,
Deadline: arg.Deadline,
MaxDeadline: arg.MaxDeadline,
Reason: arg.Reason,
}
q.workspaceBuilds = append(q.workspaceBuilds, workspaceBuild)
@ -4628,13 +4627,13 @@ func (q *fakeQuerier) GetQuotaConsumedForUser(_ context.Context, userID uuid.UUI
continue
}
var lastBuild database.WorkspaceBuildRBAC
var lastBuild database.WorkspaceBuild
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)
@ -4658,16 +4657,3 @@ func (q *fakeQuerier) UpdateWorkspaceAgentLifecycleStateByID(_ context.Context,
}
return sql.ErrNoRows
}
// expandWorkspaceThin must be called from a locked context.
func (q *fakeQuerier) expandWorkspaceThin(thin database.WorkspaceBuild) database.WorkspaceBuildRBAC {
for _, workspace := range q.workspaces {
if workspace.ID == thin.WorkspaceID {
return thin.WithWorkspace(workspace)
}
}
return database.WorkspaceBuildRBAC{
WorkspaceBuild: thin,
}
}

View File

@ -153,34 +153,20 @@ func Workspace(t testing.TB, db database.Store, orig database.Workspace) databas
return workspace
}
type AnyWorkspaceBuild interface {
database.WorkspaceBuildRBAC | database.WorkspaceBuild
}
func WorkspaceBuild[B AnyWorkspaceBuild](t testing.TB, db database.Store, orig B) database.WorkspaceBuild {
var thin database.WorkspaceBuild
switch v := any(orig).(type) {
case database.WorkspaceBuildRBAC:
thin = v.WorkspaceBuild
case database.WorkspaceBuild:
thin = v
default:
panic(fmt.Sprintf("developer error: invalid type %T", v))
}
func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuild {
build, err := db.InsertWorkspaceBuild(context.Background(), database.InsertWorkspaceBuildParams{
ID: takeFirst(thin.ID, uuid.New()),
CreatedAt: takeFirst(thin.CreatedAt, database.Now()),
UpdatedAt: takeFirst(thin.UpdatedAt, database.Now()),
WorkspaceID: takeFirst(thin.WorkspaceID, uuid.New()),
TemplateVersionID: takeFirst(thin.TemplateVersionID, uuid.New()),
BuildNumber: takeFirst(thin.BuildNumber, 1),
Transition: takeFirst(thin.Transition, database.WorkspaceTransitionStart),
InitiatorID: takeFirst(thin.InitiatorID, uuid.New()),
JobID: takeFirst(thin.JobID, uuid.New()),
ProvisionerState: takeFirstSlice(thin.ProvisionerState, []byte{}),
Deadline: takeFirst(thin.Deadline, database.Now().Add(time.Hour)),
MaxDeadline: takeFirst(thin.MaxDeadline, database.Now().Add(time.Hour*24*7)),
Reason: takeFirst(thin.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
@ -233,7 +219,7 @@ func OrganizationMember(t testing.TB, db database.Store, orig database.Organizat
UpdatedAt: takeFirst(orig.UpdatedAt, database.Now()),
Roles: takeFirstSlice(orig.Roles, []string{}),
})
require.NoError(t, err, "insert organization member")
require.NoError(t, err, "insert organization")
return mem
}

View File

@ -166,8 +166,8 @@ func TestGenerator(t *testing.T) {
t.Run("WorkspaceBuild", func(t *testing.T) {
t.Parallel()
db := dbfake.New()
exp := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuildRBAC{})
require.Equal(t, exp, must(db.GetWorkspaceBuildByID(context.Background(), exp.ID)).WorkspaceBuild)
exp := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{})
require.Equal(t, exp, must(db.GetWorkspaceBuildByID(context.Background(), exp.ID)))
})
t.Run("User", func(t *testing.T) {

View File

@ -13,14 +13,9 @@ func takeFirstIP(values ...net.IPNet) net.IPNet {
// takeFirstSlice implements takeFirst for []any.
// []any is not a comparable type.
func takeFirstSlice[T any](values ...[]T) []T {
out := takeFirstF(values, func(v []T) bool {
return takeFirstF(values, func(v []T) bool {
return len(v) != 0
})
// Prevent nil slices
if out == nil {
return []T{}
}
return out
}
// takeFirstF takes the first value that returns true

View File

@ -13,16 +13,12 @@ import (
"github.com/coder/coder/coderd/database/postgres"
)
func UsingRealDatabase() bool {
return os.Getenv("DB") != ""
}
func NewDB(t *testing.T) (database.Store, database.Pubsub) {
t.Helper()
db := dbfake.New()
pubsub := database.NewPubsubInMemory()
if UsingRealDatabase() {
if os.Getenv("DB") != "" {
connectionURL := os.Getenv("CODER_PG_CONNECTION_URL")
if connectionURL == "" {
var (

View File

@ -4,8 +4,6 @@ import (
"sort"
"strconv"
"github.com/google/uuid"
"github.com/coder/coder/coderd/rbac"
)
@ -103,12 +101,6 @@ func (g Group) RBACObject() rbac.Object {
InOrg(g.OrganizationID)
}
func (b WorkspaceBuildRBAC) 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).
@ -191,18 +183,6 @@ func (l License) RBACObject() rbac.Object {
return rbac.ResourceLicense.WithIDString(strconv.FormatInt(int64(l.ID), 10))
}
func (b WorkspaceBuild) WithWorkspace(workspace Workspace) WorkspaceBuildRBAC {
return b.Expand(workspace.OrganizationID, workspace.OwnerID)
}
func (b WorkspaceBuild) Expand(orgID, ownerID uuid.UUID) WorkspaceBuildRBAC {
return WorkspaceBuildRBAC{
WorkspaceBuild: b,
OrganizationID: orgID,
WorkspaceOwnerID: ownerID,
}
}
func ConvertUserRows(rows []GetUsersRow) []User {
users := make([]User, len(rows))
for i, r := range rows {

View File

@ -4,13 +4,11 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/lib/pq"
"golang.org/x/xerrors"
"github.com/coder/coder/coderd/database/sqlxqueries"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/rbac/regosql"
)
@ -180,95 +178,6 @@ func (q *sqlQuerier) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([
type workspaceQuerier interface {
GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspacesParams, prepared rbac.PreparedAuthorized) ([]GetWorkspacesRow, error)
GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (WorkspaceBuildRBAC, error)
GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (WorkspaceBuildRBAC, error)
GetWorkspaceBuildsCreatedAfter(ctx context.Context, after time.Time) ([]WorkspaceBuildRBAC, error)
GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (WorkspaceBuildRBAC, error)
GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg GetWorkspaceBuildsByWorkspaceIDParams) ([]WorkspaceBuildRBAC, error)
GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuildRBAC, error)
GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuildRBAC, error)
GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspacedID uuid.UUID) (WorkspaceBuildRBAC, error)
}
// WorkspaceBuildRBAC extends WorkspaceBuild with fields that are used for RBAC.
// This allows WorkspaceBuild to be used in Authorize() calls.
type WorkspaceBuildRBAC struct {
WorkspaceBuild
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
WorkspaceOwnerID uuid.UUID `db:"workspace_owner_id" json:"workspace_owner_id"`
}
type getWorkspaceBuildParams struct {
BuildID uuid.UUID `db:"build_id"`
JobID uuid.UUID `db:"job_id"`
CreatedAfter time.Time `db:"created_after"`
WorkspaceID uuid.UUID `db:"workspace_id"`
BuildNumber int32 `db:"build_number"`
LimitOpt int32 `db:"limit_opt"`
Latest bool `db:"-"`
}
func (q *sqlQuerier) getWorkspaceBuild(ctx context.Context, arg getWorkspaceBuildParams) (WorkspaceBuildRBAC, error) {
var res WorkspaceBuildRBAC
arg.LimitOpt = 1
return res, sqlxqueries.GetContext(ctx, q.db, "GetWorkspaceBuild", arg, &res)
}
func (q *sqlQuerier) selectWorkspaceBuild(ctx context.Context, arg getWorkspaceBuildParams) ([]WorkspaceBuildRBAC, error) {
var res []WorkspaceBuildRBAC
arg.LimitOpt = -1
return res, sqlxqueries.SelectContext(ctx, q.db, "GetWorkspaceBuild", arg, &res)
}
func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (WorkspaceBuildRBAC, error) {
return q.getWorkspaceBuild(ctx, getWorkspaceBuildParams{BuildID: id})
}
func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (WorkspaceBuildRBAC, error) {
return q.getWorkspaceBuild(ctx, getWorkspaceBuildParams{JobID: jobID})
}
func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, after time.Time) ([]WorkspaceBuildRBAC, error) {
return q.selectWorkspaceBuild(ctx, getWorkspaceBuildParams{CreatedAfter: after})
}
type GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams struct {
BuildNumber int32
WorkspaceID uuid.UUID
}
func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (WorkspaceBuildRBAC, error) {
return q.getWorkspaceBuild(ctx, getWorkspaceBuildParams{
BuildNumber: arg.BuildNumber,
WorkspaceID: arg.WorkspaceID,
})
}
type GetWorkspaceBuildsByWorkspaceIDParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
Since time.Time `db:"since" json:"since"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg GetWorkspaceBuildsByWorkspaceIDParams) ([]WorkspaceBuildRBAC, error) {
var res []WorkspaceBuildRBAC
return res, sqlxqueries.SelectContext(ctx, q.db, "GetWorkspaceBuildsByWorkspaceID", arg, &res)
}
func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspacedID uuid.UUID) (WorkspaceBuildRBAC, error) {
return q.getWorkspaceBuild(ctx, getWorkspaceBuildParams{WorkspaceID: workspacedID, Latest: true})
}
func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuildRBAC, error) {
var res []WorkspaceBuildRBAC
return res, sqlxqueries.SelectContext(ctx, q.db, "GetLatestWorkspaceBuildsByWorkspaceIDs", ids, &res)
}
func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuildRBAC, error) {
var res []WorkspaceBuildRBAC
return res, sqlxqueries.SelectContext(ctx, q.db, "GetLatestWorkspaceBuilds", nil, &res)
}
// GetAuthorizedWorkspaces returns all workspaces that the user is authorized to access.

View File

@ -1,189 +0,0 @@
package database_test
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbgen"
"github.com/coder/coder/coderd/database/dbtestutil"
"github.com/coder/coder/coderd/rbac"
)
func TestGetWorkspaceBuild(t *testing.T) {
t.Parallel()
if !dbtestutil.UsingRealDatabase() {
t.Skip("Test only runs against a real database")
}
db, _ := dbtestutil.NewDB(t)
// Seed the database with some workspace builds.
var (
now = database.Now()
org = dbgen.Organization(t, db, database.Organization{})
user = dbgen.User(t, db, database.User{
RBACRoles: []string{rbac.RoleOwner()},
})
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{
UserID: user.ID,
OrganizationID: org.ID,
})
template = dbgen.Template(t, db, database.Template{
OrganizationID: org.ID,
CreatedBy: user.ID,
})
version = dbgen.TemplateVersion(t, db, database.TemplateVersion{
TemplateID: uuid.NullUUID{
UUID: template.ID,
Valid: true,
},
OrganizationID: org.ID,
CreatedBy: user.ID,
})
workspace = dbgen.Workspace(t, db, database.Workspace{
OwnerID: user.ID,
OrganizationID: org.ID,
TemplateID: template.ID,
})
jobs = []database.ProvisionerJob{
dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
OrganizationID: org.ID,
InitiatorID: user.ID,
}),
dbgen.ProvisionerJob(t, db, database.ProvisionerJob{
OrganizationID: org.ID,
InitiatorID: user.ID,
}),
}
builds = []database.WorkspaceBuild{
dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
WorkspaceID: workspace.ID,
TemplateVersionID: version.ID,
BuildNumber: 1,
Transition: database.WorkspaceTransitionStart,
InitiatorID: user.ID,
JobID: jobs[0].ID,
Reason: database.BuildReasonInitiator,
CreatedAt: now,
}),
dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
WorkspaceID: workspace.ID,
TemplateVersionID: version.ID,
BuildNumber: 2,
Transition: database.WorkspaceTransitionStart,
InitiatorID: user.ID,
JobID: jobs[1].ID,
Reason: database.BuildReasonInitiator,
CreatedAt: now.Add(time.Hour),
}),
}
orderBuilds = []database.WorkspaceBuild{
builds[1],
builds[0],
}
ctx = context.Background()
)
t.Run("GetWorkspaceBuildByID", func(t *testing.T) {
t.Parallel()
for _, expected := range builds {
build, err := db.GetWorkspaceBuildByID(ctx, expected.ID)
if err != nil {
t.Fatal(err)
}
require.Equal(t, expected, build.WorkspaceBuild, "builds should be equal")
}
})
t.Run("GetWorkspaceBuildByJobID", func(t *testing.T) {
t.Parallel()
for i, job := range jobs {
build, err := db.GetWorkspaceBuildByJobID(ctx, job.ID)
if err != nil {
t.Fatal(err)
}
expected := builds[i]
require.Equal(t, expected, build.WorkspaceBuild, "builds should be equal")
}
})
t.Run("GetWorkspaceBuildsCreatedAfter", func(t *testing.T) {
t.Parallel()
found, err := db.GetWorkspaceBuildsCreatedAfter(ctx, builds[0].CreatedAt.Add(time.Second))
if err != nil {
t.Fatal(err)
}
expected := builds[1]
require.Len(t, found, 1, "should only be one build")
require.Equal(t, expected, found[0].WorkspaceBuild, "builds should be equal")
})
t.Run("GetWorkspaceBuildByWorkspaceIDAndBuildNumber", func(t *testing.T) {
t.Parallel()
for _, expected := range builds {
build, err := db.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx, database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams{
BuildNumber: expected.BuildNumber,
WorkspaceID: expected.WorkspaceID,
})
if err != nil {
t.Fatal(err)
}
require.Equal(t, expected, build.WorkspaceBuild, "builds should be equal")
}
})
t.Run("GetWorkspaceBuildsByWorkspaceID", func(t *testing.T) {
t.Parallel()
found, err := db.GetWorkspaceBuildsByWorkspaceID(ctx, database.GetWorkspaceBuildsByWorkspaceIDParams{
WorkspaceID: workspace.ID,
Since: builds[0].CreatedAt.Add(-1 * time.Hour),
})
if err != nil {
t.Fatal(err)
}
require.Len(t, found, 2, "should be two builds")
require.Equal(t, orderBuilds, toThins(found), "builds should be equal")
})
t.Run("GetLatestWorkspaceBuildsByWorkspaceIDs", func(t *testing.T) {
t.Parallel()
found, err := db.GetLatestWorkspaceBuildsByWorkspaceIDs(ctx, []uuid.UUID{workspace.ID})
if err != nil {
t.Fatal(err)
}
require.Len(t, found, 1, "should be only one build")
require.Equal(t, builds[1], found[0].WorkspaceBuild, "builds should be equal")
})
t.Run("GetLatestWorkspaceBuilds", func(t *testing.T) {
t.Parallel()
found, err := db.GetLatestWorkspaceBuilds(ctx)
if err != nil {
t.Fatal(err)
}
require.Len(t, found, 1, "should be only 1 build")
require.Equal(t, builds[1], found[0].WorkspaceBuild, "builds should be equal")
})
t.Run("GetLatestWorkspaceBuildByWorkspaceID", func(t *testing.T) {
t.Parallel()
found, err := db.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
if err != nil {
t.Fatal(err)
}
require.Equal(t, builds[1], found.WorkspaceBuild, "builds should be equal")
})
}
func toThins(builds []database.WorkspaceBuildRBAC) []database.WorkspaceBuild {
thins := make([]database.WorkspaceBuild, len(builds))
for i, build := range builds {
thins[i] = build.WorkspaceBuild
}
return thins
}

View File

@ -66,6 +66,9 @@ type sqlcQuerier interface {
GetGroupMembers(ctx context.Context, groupID uuid.UUID) ([]User, error)
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)
GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error)
GetLicenseByID(ctx context.Context, id int32) (License, error)
GetLicenses(ctx context.Context) ([]License, error)
GetLogoURL(ctx context.Context) (string, error)
@ -124,7 +127,12 @@ type sqlcQuerier interface {
GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]WorkspaceApp, error)
GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceApp, error)
GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceApp, error)
GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (WorkspaceBuild, error)
GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (WorkspaceBuild, error)
GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (WorkspaceBuild, error)
GetWorkspaceBuildParameters(ctx context.Context, workspaceBuildID uuid.UUID) ([]WorkspaceBuildParameter, error)
GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg GetWorkspaceBuildsByWorkspaceIDParams) ([]WorkspaceBuild, error)
GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceBuild, error)
GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUID) (Workspace, error)
GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Workspace, error)
GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWorkspaceByOwnerIDAndNameParams) (Workspace, error)

View File

@ -6033,6 +6033,380 @@ func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg Ins
return err
}
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, max_deadline
FROM
workspace_builds
WHERE
workspace_id = $1
ORDER BY
build_number desc
LIMIT
1
`
func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getLatestWorkspaceBuildByWorkspaceID, workspaceID)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
)
return i, err
}
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, wb.max_deadline
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_builds
GROUP BY
workspace_id
) m
JOIN
workspace_builds wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
`
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 []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
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, wb.max_deadline
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_builds
WHERE
workspace_id = ANY($1 :: uuid [ ])
GROUP BY
workspace_id
) m
JOIN
workspace_builds wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
`
func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getLatestWorkspaceBuildsByWorkspaceIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
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, max_deadline
FROM
workspace_builds
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceBuildByID, id)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
)
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, max_deadline
FROM
workspace_builds
WHERE
job_id = $1
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceBuildByJobID, jobID)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
)
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, max_deadline
FROM
workspace_builds
WHERE
workspace_id = $1
AND build_number = $2
`
type GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
BuildNumber int32 `db:"build_number" json:"build_number"`
}
func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceBuildByWorkspaceIDAndBuildNumber, arg.WorkspaceID, arg.BuildNumber)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
)
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, max_deadline
FROM
workspace_builds
WHERE
workspace_builds.workspace_id = $1
AND workspace_builds.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.
WHEN $3 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN (
-- The pagination cursor is the last ID of the previous page.
-- The query is ordered by the build_number field, so select all
-- rows after the cursor.
build_number > (
SELECT
build_number
FROM
workspace_builds
WHERE
id = $3
)
)
ELSE true
END
ORDER BY
build_number desc OFFSET $4
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($5 :: int, 0)
`
type GetWorkspaceBuildsByWorkspaceIDParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
Since time.Time `db:"since" json:"since"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg GetWorkspaceBuildsByWorkspaceIDParams) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceBuildsByWorkspaceID,
arg.WorkspaceID,
arg.Since,
arg.AfterID,
arg.OffsetOpt,
arg.LimitOpt,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
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, max_deadline FROM workspace_builds WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceBuildsCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceBuild = `-- name: InsertWorkspaceBuild :one
INSERT INTO
workspace_builds (

View File

@ -1,3 +1,110 @@
-- name: GetWorkspaceBuildByID :one
SELECT
*
FROM
workspace_builds
WHERE
id = $1
LIMIT
1;
-- name: GetWorkspaceBuildByJobID :one
SELECT
*
FROM
workspace_builds
WHERE
job_id = $1
LIMIT
1;
-- name: GetWorkspaceBuildsCreatedAfter :many
SELECT * FROM workspace_builds WHERE created_at > $1;
-- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one
SELECT
*
FROM
workspace_builds
WHERE
workspace_id = $1
AND build_number = $2;
-- name: GetWorkspaceBuildsByWorkspaceID :many
SELECT
*
FROM
workspace_builds
WHERE
workspace_builds.workspace_id = $1
AND workspace_builds.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
-- duplicating or missing data.
WHEN @after_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN (
-- The pagination cursor is the last ID of the previous page.
-- The query is ordered by the build_number field, so select all
-- rows after the cursor.
build_number > (
SELECT
build_number
FROM
workspace_builds
WHERE
id = @after_id
)
)
ELSE true
END
ORDER BY
build_number desc OFFSET @offset_opt
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF(@limit_opt :: int, 0);
-- name: GetLatestWorkspaceBuildByWorkspaceID :one
SELECT
*
FROM
workspace_builds
WHERE
workspace_id = $1
ORDER BY
build_number desc
LIMIT
1;
-- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many
SELECT wb.*
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_builds
WHERE
workspace_id = ANY(@ids :: uuid [ ])
GROUP BY
workspace_id
) m
JOIN
workspace_builds wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number;
-- name: GetLatestWorkspaceBuilds :many
SELECT wb.*
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_builds
GROUP BY
workspace_id
) m
JOIN
workspace_builds wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number;
-- name: InsertWorkspaceBuild :one
INSERT INTO
workspace_builds (

View File

@ -1,35 +0,0 @@
# Editor/IDE config
To edit template files, it is best to configure your IDE to work with go template files. VSCode gives better highlighting support, as the Goland highlighting tends to recognize the sql as invalid and shows many sql errors in the template file.
## VSCode
Required extension (Default Golang Extension): https://marketplace.visualstudio.com/items?itemName=golang.Go
The default extension [supports syntax highlighting](https://github.com/golang/vscode-go/wiki/features#go-template-syntax-highlighting), but requires a configuration change. You must add this section to your golang extension settings:
```json
"gopls": {
"ui.semanticTokens": true
},
```
The VSCode extension does not support both go template and postgres highlighting. I suggest you use Postgres highlighting, as it is much easier to work with. You can switch between the two with:
1. `ctl + shift + p`
1. "Change language Mode"
1. "Postgres" or "Go Template File"
- Feel free to create a permanent file association with `*.gosql` files.
## Goland
Goland supports [template highlighting](https://www.jetbrains.com/help/go/integration-with-go-templates.html) out of the box. To associate sql files, add a new file type in **Editor** settings. Select "Go template files". Add a new filename of `*.gosql` and select "postgres" as the "Template Data Language".
![Goland language configuration](./imgs/goland-gosql.png)
It also helps to support the sqlc type variables. You can do this by adding ["User Parameters"](https://www.jetbrains.com/help/datagrip/settings-tools-database-user-parameters.html) in database queries.
![Goland language configuration](./imgs/goland-user-params.png)
You can also add `dump.sql` as a DDL data source for proper table column recognition.

View File

@ -1,95 +0,0 @@
package sqlxqueries
import (
"fmt"
"reflect"
"regexp"
"strings"
"golang.org/x/xerrors"
"github.com/google/uuid"
"github.com/jmoiron/sqlx/reflectx"
"github.com/lib/pq"
"github.com/coder/coder/coderd/util/slice"
)
var nameRegex = regexp.MustCompile(`@([a-zA-Z0-9_]+)`)
// dbmapper grabs struct 'db' tags.
var dbmapper = reflectx.NewMapper("db")
// bindNamed is an implementation that improves on the SQLx implementation. This
// adjusts the query to use "$#" syntax for arguments instead of "@argument". The
// returned args are the values of the struct fields that match the names in the
// correct order and indexing.
//
// 1. SQLx does not reuse arguments, so "@arg, @arg" will result in two arguments
// "$1, $2" instead of "$1, $1".
// 2. SQLx does not handle uuid arrays.
// 3. SQLx only supports ":name" style arguments and breaks "::" type casting.
func bindNamed(query string, arg interface{}) (newQuery string, args []interface{}, err error) {
// We do not need to implement a sql parser to extract and replace the variable names.
// All names follow a simple regex.
names := nameRegex.FindAllString(query, -1)
// Get all unique names
names = slice.Unique(names)
// Replace all names with the correct index
for i, name := range names {
rpl := fmt.Sprintf("$%d", i+1)
if strings.Contains(query, rpl) {
return "", nil,
xerrors.Errorf("query contains both named params %q, and unnamed %q: choose one", name, rpl)
}
query = strings.ReplaceAll(query, name, rpl)
// Remove the "@" prefix to match to the "db" struct tag.
names[i] = strings.TrimPrefix(name, "@")
}
arglist := make([]interface{}, 0, len(names))
// This comes straight from SQLx's implementation to get the values
// of the struct fields.
var v reflect.Value
for v = reflect.ValueOf(arg); v.Kind() == reflect.Ptr; {
v = v.Elem()
}
// If there is only 1 argument, and the argument is not a struct, then
// the only argument is the value passed in. This is a nice shortcut
// for simple queries with 1 param like "id".
if v.Type().Kind() != reflect.Struct && len(names) == 1 {
arglist = append(arglist, pqValue(v))
return query, arglist, nil
}
err = dbmapper.TraversalsByNameFunc(v.Type(), names, func(i int, t []int) error {
if len(t) == 0 {
return xerrors.Errorf("could not find name %s in %#v", names[i], arg)
}
val := reflectx.FieldByIndexesReadOnly(v, t)
arglist = append(arglist, pqValue(val))
return nil
})
if err != nil {
return "", nil, err
}
return query, arglist, nil
}
func pqValue(val reflect.Value) interface{} {
valI := val.Interface()
// Handle some custom types to make arguments easier to use.
switch valI.(type) {
// Feel free to add more types here as needed.
case []uuid.UUID:
return pq.Array(valI)
default:
return valI
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,72 +0,0 @@
package sqlxqueries
import (
"context"
"github.com/jmoiron/sqlx"
"golang.org/x/xerrors"
)
// constructQuery will return a SQL query by the given template name.
// It will also return the arguments in order for the query based on the input
// argument.
func constructQuery(queryName string, argument any) (string, []any, error) {
// No argument was given, use an empty struct.
if argument == nil {
argument = struct{}{}
}
query, err := query(queryName, argument)
if err != nil {
return "", nil, xerrors.Errorf("get query: %w", err)
}
query, args, err := bindNamed(query, argument)
if err != nil {
return "", nil, xerrors.Errorf("bind named: %w", err)
}
return query, args, nil
}
// SelectContext runs the named query on the given database.
// If the query returns no rows, an empty slice is returned.
func SelectContext(ctx context.Context, q sqlx.QueryerContext, queryName string, argument any, res any) error {
if q == nil {
return xerrors.New("queryer is nil")
}
query, args, err := constructQuery(queryName, argument)
if err != nil {
return xerrors.Errorf("get query: %w", err)
}
err = sqlx.SelectContext(ctx, q, res, query, args...)
if err != nil {
return xerrors.Errorf("%s: %w", queryName, err)
}
return nil
}
// GetContext runs the named query on the given database.
// If the query returns no rows, sql.ErrNoRows is returned.
func GetContext(ctx context.Context, q sqlx.QueryerContext, queryName string, argument interface{}, res any) error {
if q == nil {
return xerrors.New("queryer is nil")
}
query, args, err := constructQuery(queryName, argument)
if err != nil {
return xerrors.Errorf("get query: %w", err)
}
// GetContext maps the results of the query to the items slice by struct
// db tags.
err = sqlx.GetContext(ctx, q, res, query, args...)
if err != nil {
return xerrors.Errorf("%s: %w", queryName, err)
}
return nil
}

View File

@ -1,55 +0,0 @@
package sqlxqueries
import (
"bytes"
"embed"
"sync"
"text/template"
"golang.org/x/xerrors"
)
//go:embed *.gosql
var sqlxQueries embed.FS
var (
// Only parse the queries once.
once sync.Once
cached *template.Template
//nolint:errname
cachedError error
)
// LoadQueries parses the embedded queries and returns the template.
// Results are cached.
func LoadQueries() (*template.Template, error) {
once.Do(func() {
tpls, err := template.New("").
Funcs(template.FuncMap{
"int32": func(i int) int32 { return int32(i) },
}).ParseFS(sqlxQueries, "*.gosql")
if err != nil {
cachedError = xerrors.Errorf("developer error parse sqlx queries: %w", err)
return
}
cached = tpls
})
return cached, cachedError
}
// query executes the named template with the given data and returns the result.
// The returned query string is SQL.
func query(name string, data interface{}) (string, error) {
tpls, err := LoadQueries()
if err != nil {
return "", err
}
var out bytes.Buffer
err = tpls.ExecuteTemplate(&out, name, data)
if err != nil {
return "", xerrors.Errorf("execute template %s: %w", name, err)
}
return out.String(), nil
}

View File

@ -1,15 +0,0 @@
package sqlxqueries_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/database/sqlxqueries"
)
func Test_loadQueries(t *testing.T) {
t.Parallel()
_, err := sqlxqueries.LoadQueries()
require.NoError(t, err)
}

View File

@ -1,128 +0,0 @@
{{ define "workspace_builds_rbac" }}
(
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
)
{{ end }};
{{ define "GetWorkspaceBuild" }}
-- name: GetWorkspaceBuild :one
SELECT
*
FROM
{{ template "workspace_builds_rbac" }} workspace_builds
WHERE
CASE
WHEN @build_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
id = @build_id
ELSE true
END
AND CASE
WHEN @job_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
job_id = @job_id
ELSE true
END
AND CASE
WHEN @job_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
job_id = @job_id
ELSE true
END
AND CASE
WHEN @created_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
created_at > @created_after
ELSE true
END
AND CASE
WHEN @workspace_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
workspace_id = @workspace_id
ELSE true
END
AND CASE
WHEN @build_number :: integer != 0 THEN
build_number = @build_number
ELSE true
END
{{ if .Latest }}
ORDER BY
build_number desc
{{ end }}
{{ if gt .LimitOpt 0 }} LIMIT @limit_opt {{ end }}
;
{{ end }}
{{ define "GetWorkspaceBuildsByWorkspaceID" }}
-- name: GetWorkspaceBuildsByWorkspaceID :many
SELECT
*
FROM
{{ template "workspace_builds_rbac" }} workspace_builds
WHERE
workspace_builds.workspace_id = @workspace_id
AND workspace_builds.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
-- duplicating or missing data.
WHEN @after_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN (
-- The pagination cursor is the last ID of the previous page.
-- The query is ordered by the build_number field, so select all
-- rows after the cursor.
build_number > (
SELECT
build_number
FROM
workspace_builds
WHERE
id = @after_id
)
)
ELSE true
END
ORDER BY
build_number desc OFFSET @offset_opt
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF(@limit_opt :: int, 0);
{{ end }}
{{ define "GetLatestWorkspaceBuildsByWorkspaceIDs" }}
-- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many
SELECT wb.*
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_builds
WHERE
workspace_id = ANY(@ids :: uuid [ ])
GROUP BY
workspace_id
) m
JOIN
{{ template "workspace_builds_rbac" }} wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number;
{{ end }}
{{ define "GetLatestWorkspaceBuilds" }}
-- name: GetLatestWorkspaceBuilds :many
SELECT wb.*
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_builds
GROUP BY
workspace_id
) m
JOIN
{{ template "workspace_builds_rbac" }} wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number;
{{ end }}

View File

@ -16,8 +16,8 @@ import (
type workspaceBuildParamContextKey struct{}
// WorkspaceBuildParam returns the workspace build from the ExtractWorkspaceBuildParam handler.
func WorkspaceBuildParam(r *http.Request) database.WorkspaceBuildRBAC {
workspaceBuild, ok := r.Context().Value(workspaceBuildParamContextKey{}).(database.WorkspaceBuildRBAC)
func WorkspaceBuildParam(r *http.Request) database.WorkspaceBuild {
workspaceBuild, ok := r.Context().Value(workspaceBuildParamContextKey{}).(database.WorkspaceBuild)
if !ok {
panic("developer error: workspace build param middleware not provided")
}

View File

@ -670,14 +670,14 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p
return nil, xerrors.Errorf("unmarshal workspace provision input: %w", err)
}
var build database.WorkspaceBuildRBAC
var build database.WorkspaceBuild
err := server.Database.InTx(func(db database.Store) error {
workspaceBuild, err := db.GetWorkspaceBuildByID(ctx, input.WorkspaceBuildID)
if err != nil {
return xerrors.Errorf("get workspace build: %w", err)
}
thinBuild, err := db.UpdateWorkspaceBuildByID(ctx, database.UpdateWorkspaceBuildByIDParams{
build, err = db.UpdateWorkspaceBuildByID(ctx, database.UpdateWorkspaceBuildByIDParams{
ID: input.WorkspaceBuildID,
UpdatedAt: database.Now(),
ProvisionerState: jobType.WorkspaceBuild.State,
@ -687,8 +687,6 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p
if err != nil {
return xerrors.Errorf("update workspace build state: %w", err)
}
// Keep the same owner args as the original build.
build = thinBuild.Expand(workspaceBuild.OrganizationID, workspaceBuild.WorkspaceOwnerID)
return nil
}, nil)
@ -721,7 +719,7 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p
BuildNumber: previousBuildNumber,
})
if prevBuildErr != nil {
previousBuild = database.WorkspaceBuildRBAC{}
previousBuild = database.WorkspaceBuild{}
}
// We pass the below information to the Auditor so that it
@ -737,7 +735,7 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p
server.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
}
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuildRBAC]{
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
Audit: *auditor,
Log: server.Logger,
UserID: job.InitiatorID,
@ -1041,7 +1039,7 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete
BuildNumber: previousBuildNumber,
})
if prevBuildErr != nil {
previousBuild = database.WorkspaceBuildRBAC{}
previousBuild = database.WorkspaceBuild{}
}
// We pass the below information to the Auditor so that it
@ -1057,7 +1055,7 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete
server.Logger.Error(ctx, "marshal resource info for successful job", slog.Error(err))
}
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuildRBAC]{
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
Audit: *auditor,
Log: server.Logger,
UserID: job.InitiatorID,

View File

@ -644,14 +644,13 @@ func TestFailJob(t *testing.T) {
ID: uuid.New(),
})
require.NoError(t, err)
buildThin, err := srv.Database.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
build, 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,
})

View File

@ -503,7 +503,7 @@ func ConvertWorkspace(workspace database.Workspace) Workspace {
}
// ConvertWorkspaceBuild anonymizes a workspace build.
func ConvertWorkspaceBuild(build database.WorkspaceBuildRBAC) WorkspaceBuild {
func ConvertWorkspaceBuild(build database.WorkspaceBuild) WorkspaceBuild {
return WorkspaceBuild{
ID: build.ID,
CreatedAt: build.CreatedAt,

View File

@ -41,7 +41,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
return
}
data, err := api.workspaceBuildsData(ctx, []database.Workspace{workspace}, []database.WorkspaceBuildRBAC{workspaceBuild})
data, err := api.workspaceBuildsData(ctx, []database.Workspace{workspace}, []database.WorkspaceBuild{workspaceBuild})
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error getting workspace build data.",
@ -113,7 +113,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
}
}
var workspaceBuilds []database.WorkspaceBuildRBAC
var workspaceBuilds []database.WorkspaceBuild
// Ensure all db calls happen in the same tx
err := api.Database.InTx(func(store database.Store) error {
var err error
@ -253,7 +253,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
return
}
data, err := api.workspaceBuildsData(ctx, []database.Workspace{workspace}, []database.WorkspaceBuildRBAC{workspaceBuild})
data, err := api.workspaceBuildsData(ctx, []database.Workspace{workspace}, []database.WorkspaceBuild{workspaceBuild})
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error getting workspace build data.",
@ -526,7 +526,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
var workspaceBuild database.WorkspaceBuildRBAC
var workspaceBuild database.WorkspaceBuild
var provisionerJob database.ProvisionerJob
// This must happen in a transaction to ensure history can be inserted, and
// the prior history can update it's "after" column to point at the new.
@ -584,7 +584,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return xerrors.Errorf("insert provisioner job: %w", err)
}
thinBuild, err := db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
workspaceBuild, err = db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
ID: workspaceBuildID,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
@ -601,9 +601,6 @@ 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 {
@ -926,7 +923,7 @@ type workspaceBuildsData struct {
apps []database.WorkspaceApp
}
func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.Workspace, workspaceBuilds []database.WorkspaceBuildRBAC) (workspaceBuildsData, error) {
func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.Workspace, workspaceBuilds []database.WorkspaceBuild) (workspaceBuildsData, error) {
userIDs := make([]uuid.UUID, 0, len(workspaceBuilds))
for _, build := range workspaceBuilds {
userIDs = append(userIDs, build.InitiatorID)
@ -1017,7 +1014,7 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W
}
func (api *API) convertWorkspaceBuilds(
workspaceBuilds []database.WorkspaceBuildRBAC,
workspaceBuilds []database.WorkspaceBuild,
workspaces []database.Workspace,
jobs []database.ProvisionerJob,
users []database.User,
@ -1078,7 +1075,7 @@ func (api *API) convertWorkspaceBuilds(
}
func (api *API) convertWorkspaceBuild(
build database.WorkspaceBuildRBAC,
build database.WorkspaceBuild,
workspace database.Workspace,
job database.ProvisionerJob,
users []database.User,

View File

@ -475,7 +475,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
tags := provisionerdserver.MutateTags(user.ID, templateVersionJob.Tags)
var (
provisionerJob database.ProvisionerJob
workspaceBuild database.WorkspaceBuildRBAC
workspaceBuild database.WorkspaceBuild
)
err = api.Database.InTx(func(db database.Store) error {
now := database.Now()
@ -540,7 +540,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
if err != nil {
return xerrors.Errorf("insert provisioner job: %w", err)
}
workspaceBuildThin, err := db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
workspaceBuild, err = db.InsertWorkspaceBuild(ctx, database.InsertWorkspaceBuildParams{
ID: workspaceBuildID,
CreatedAt: now,
UpdatedAt: now,
@ -556,7 +556,6 @@ 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))

View File

@ -19,7 +19,7 @@ We track the following resources:
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>git_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>false</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| Workspace<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr><tr><td>workspace_owner_id</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |
<!-- End generated by 'make docs/admin/audit-logs.md'. -->

View File

@ -126,8 +126,6 @@ var AuditableResources = auditMap(map[any]map[string]Action{
"reason": ActionIgnore,
"daily_cost": ActionIgnore,
"max_deadline": ActionIgnore,
"organization_id": ActionIgnore,
"workspace_owner_id": ActionIgnore,
},
&database.AuditableGroup{}: {
"id": ActionTrack,