From 2fa1ef377551d5fc0e0f7ac98cdd9dedf8bb1aba Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Thu, 9 Nov 2023 08:09:07 +0000 Subject: [PATCH 1/9] chore: refactor ResolveAutostart tests to use dbfake --- coderd/database/dbfake/dbfake.go | 120 +++- coderd/database/dbgen/dbgen.go | 579 ++++++++++-------- coderd/database/dbgen/dbgen_test.go | 16 + coderd/workspaces_test.go | 147 +++-- .../coderd/coderdenttest/coderdenttest.go | 6 + enterprise/coderd/workspaces_test.go | 72 +-- 6 files changed, 541 insertions(+), 399 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 9d7e0946d64a9..010332128a979 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/provisionerdserver" "github.com/coder/coder/v2/coderd/telemetry" + "github.com/coder/coder/v2/provisionersdk/proto" sdkproto "github.com/coder/coder/v2/provisionersdk/proto" ) @@ -37,6 +38,64 @@ func Workspace(t testing.TB, db database.Store, seed database.Workspace) databas return dbgen.Workspace(t, db, seed) } +func TemplateWithVersion(t testing.TB, db database.Store, tpl database.Template, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*proto.Resource) (database.Template, database.TemplateVersion) { + t.Helper() + + template := dbgen.Template(t, db, tpl) + + tv.TemplateID = dbgen.TakeFirst(tv.TemplateID, uuid.NullUUID{UUID: template.ID, Valid: true}) + tv.OrganizationID = dbgen.TakeFirst(tv.OrganizationID, template.OrganizationID) + tv.CreatedBy = dbgen.TakeFirst(tv.CreatedBy, template.CreatedBy) + version := TemplateVersion(t, db, tv, job, resources...) + + err := db.UpdateTemplateActiveVersionByID(dbgen.Ctx, database.UpdateTemplateActiveVersionByIDParams{ + ID: template.ID, + ActiveVersionID: version.ID, + UpdatedAt: dbtime.Now(), + }) + require.NoError(t, err) + + return template, version +} + +func TemplateVersion(t testing.TB, db database.Store, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*proto.Resource) database.TemplateVersion { + templateVersion := dbgen.TemplateVersion(t, db, tv) + payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{ + TemplateVersionID: templateVersion.ID, + }) + require.NoError(t, err) + + job.ID = dbgen.TakeFirst(job.ID, templateVersion.JobID) + job.OrganizationID = dbgen.TakeFirst(job.OrganizationID, templateVersion.OrganizationID) + job.Input = dbgen.TakeFirstSlice(job.Input, payload) + job.Type = dbgen.TakeFirst(job.Type, database.ProvisionerJobTypeTemplateVersionImport) + job.CompletedAt = dbgen.TakeFirst(job.CompletedAt, sql.NullTime{ + Time: dbtime.Now(), + Valid: true, + }) + + job = dbgen.ProvisionerJob(t, db, nil, job) + ProvisionerJobResources(t, db, job.ID, "", resources...) + return templateVersion +} + +func TemplateVersionWithParams(t testing.TB, db database.Store, tv database.TemplateVersion, job database.ProvisionerJob, params []database.TemplateVersionParameter) (database.TemplateVersion, []database.TemplateVersionParameter) { + t.Helper() + + version := TemplateVersion(t, db, tv, job) + tvps := make([]database.TemplateVersionParameter, 0, len(params)) + + for _, param := range params { + if param.TemplateVersionID == uuid.Nil { + param.TemplateVersionID = version.ID + } + tvp := dbgen.TemplateVersionParameter(t, db, param) + tvps = append(tvps, tvp) + } + + return version, tvps +} + // WorkspaceWithAgent is a helper that generates a workspace with a single resource // that has an agent attached to it. The agent token is returned. func WorkspaceWithAgent(t testing.TB, db database.Store, seed database.Workspace) (database.Workspace, string) { @@ -56,6 +115,19 @@ func WorkspaceWithAgent(t testing.TB, db database.Store, seed database.Workspace return ws, authToken } +func WorkspaceBuildWithParameters(t testing.TB, db database.Store, ws database.Workspace, build database.WorkspaceBuild, params []database.WorkspaceBuildParameter, resources ...*sdkproto.Resource) (database.WorkspaceBuild, []database.WorkspaceBuildParameter) { + t.Helper() + + b := WorkspaceBuild(t, db, ws, build, resources...) + + for i, param := range params { + if param.WorkspaceBuildID == uuid.Nil { + params[i].WorkspaceBuildID = b.ID + } + } + return b, dbgen.WorkspaceBuildParameters(t, db, params) +} + // WorkspaceBuild inserts a build and a successful job into the database. func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace, seed database.WorkspaceBuild, resources ...*sdkproto.Resource) database.WorkspaceBuild { t.Helper() @@ -69,9 +141,7 @@ func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace, seed WorkspaceBuildID: seed.ID, }) require.NoError(t, err) - //nolint:gocritic // This is only used by tests. - ctx := dbauthz.AsSystemRestricted(context.Background()) - job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{ + job, err := db.InsertProvisionerJob(dbgen.Ctx, database.InsertProvisionerJobParams{ ID: jobID, CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), @@ -86,7 +156,7 @@ func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace, seed TraceMetadata: pqtype.NullRawMessage{}, }) require.NoError(t, err, "insert job") - err = db.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ + err = db.UpdateProvisionerJobWithCompleteByID(dbgen.Ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: job.ID, UpdatedAt: dbtime.Now(), Error: sql.NullString{}, @@ -101,30 +171,14 @@ func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace, seed // This intentionally fulfills the minimum requirements of the schema. // Tests can provide a custom version ID if necessary. if seed.TemplateVersionID == uuid.Nil { - jobID := uuid.New() - templateVersion := dbgen.TemplateVersion(t, db, database.TemplateVersion{ - JobID: jobID, + templateVersion := TemplateVersion(t, db, database.TemplateVersion{ OrganizationID: ws.OrganizationID, CreatedBy: ws.OwnerID, TemplateID: uuid.NullUUID{ UUID: ws.TemplateID, Valid: true, }, - }) - payload, _ := json.Marshal(provisionerdserver.TemplateVersionImportJob{ - TemplateVersionID: templateVersion.ID, - }) - dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ - ID: jobID, - OrganizationID: ws.OrganizationID, - Input: payload, - Type: database.ProvisionerJobTypeTemplateVersionImport, - CompletedAt: sql.NullTime{ - Time: dbtime.Now(), - Valid: true, - }, - }) - ProvisionerJobResources(t, db, jobID, seed.Transition, resources...) + }, database.ProvisionerJob{}) seed.TemplateVersionID = templateVersion.ID } build := dbgen.WorkspaceBuild(t, db, seed) @@ -145,3 +199,25 @@ func ProvisionerJobResources(t testing.TB, db database.Store, job uuid.UUID, tra require.NoError(t, err) } } + +func must[V any](v V, err error) V { + if err != nil { + panic(err) + } + return v +} + +// takeFirstF takes the first value that returns true +func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { + for _, v := range values { + if take(v) { + return v + } + } + // If all empty, return the last element + if len(values) > 0 { + return values[len(values)-1] + } + var empty Value + return empty +} diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 970709fec9087..34869ab0b1692 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -28,8 +28,8 @@ import ( // All methods take in a 'seed' object. Any provided fields in the seed will be // maintained. Any fields omitted will have sensible defaults generated. -// genCtx is to give all generator functions permission if the db is a dbauthz db. -var genCtx = dbauthz.As(context.Background(), rbac.Subject{ +// Ctx is to give all generator functions permission if the db is a dbauthz db. +var Ctx = dbauthz.As(context.Background(), rbac.Subject{ ID: "owner", Roles: rbac.Roles(must(rbac.RoleNames{rbac.RoleOwner()}.Expand())), Groups: []string{}, @@ -37,60 +37,60 @@ var genCtx = dbauthz.As(context.Background(), rbac.Subject{ }) func AuditLog(t testing.TB, db database.Store, seed database.AuditLog) database.AuditLog { - log, err := db.InsertAuditLog(genCtx, database.InsertAuditLogParams{ - ID: takeFirst(seed.ID, uuid.New()), - Time: takeFirst(seed.Time, dbtime.Now()), - UserID: takeFirst(seed.UserID, uuid.New()), - OrganizationID: takeFirst(seed.OrganizationID, uuid.New()), + log, err := db.InsertAuditLog(Ctx, database.InsertAuditLogParams{ + ID: TakeFirst(seed.ID, uuid.New()), + Time: TakeFirst(seed.Time, dbtime.Now()), + UserID: TakeFirst(seed.UserID, uuid.New()), + OrganizationID: TakeFirst(seed.OrganizationID, uuid.New()), Ip: pqtype.Inet{ IPNet: takeFirstIP(seed.Ip.IPNet, net.IPNet{}), - Valid: takeFirst(seed.Ip.Valid, false), + Valid: TakeFirst(seed.Ip.Valid, false), }, UserAgent: sql.NullString{ - String: takeFirst(seed.UserAgent.String, ""), - Valid: takeFirst(seed.UserAgent.Valid, false), + String: TakeFirst(seed.UserAgent.String, ""), + Valid: TakeFirst(seed.UserAgent.Valid, false), }, - ResourceType: takeFirst(seed.ResourceType, database.ResourceTypeOrganization), - ResourceID: takeFirst(seed.ResourceID, uuid.New()), - ResourceTarget: takeFirst(seed.ResourceTarget, uuid.NewString()), - Action: takeFirst(seed.Action, database.AuditActionCreate), - Diff: takeFirstSlice(seed.Diff, []byte("{}")), - StatusCode: takeFirst(seed.StatusCode, 200), - AdditionalFields: takeFirstSlice(seed.Diff, []byte("{}")), - RequestID: takeFirst(seed.RequestID, uuid.New()), - ResourceIcon: takeFirst(seed.ResourceIcon, ""), + ResourceType: TakeFirst(seed.ResourceType, database.ResourceTypeOrganization), + ResourceID: TakeFirst(seed.ResourceID, uuid.New()), + ResourceTarget: TakeFirst(seed.ResourceTarget, uuid.NewString()), + Action: TakeFirst(seed.Action, database.AuditActionCreate), + Diff: TakeFirstSlice(seed.Diff, []byte("{}")), + StatusCode: TakeFirst(seed.StatusCode, 200), + AdditionalFields: TakeFirstSlice(seed.Diff, []byte("{}")), + RequestID: TakeFirst(seed.RequestID, uuid.New()), + ResourceIcon: TakeFirst(seed.ResourceIcon, ""), }) require.NoError(t, err, "insert audit log") return log } func Template(t testing.TB, db database.Store, seed database.Template) database.Template { - id := takeFirst(seed.ID, uuid.New()) + id := TakeFirst(seed.ID, uuid.New()) if seed.GroupACL == nil { // By default, all users in the organization can read the template. seed.GroupACL = database.TemplateACL{ seed.OrganizationID.String(): []rbac.Action{rbac.ActionRead}, } } - err := db.InsertTemplate(genCtx, database.InsertTemplateParams{ + err := db.InsertTemplate(Ctx, database.InsertTemplateParams{ ID: id, - CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(seed.UpdatedAt, dbtime.Now()), - OrganizationID: takeFirst(seed.OrganizationID, uuid.New()), - Name: takeFirst(seed.Name, namesgenerator.GetRandomName(1)), - Provisioner: takeFirst(seed.Provisioner, database.ProvisionerTypeEcho), - ActiveVersionID: takeFirst(seed.ActiveVersionID, uuid.New()), - Description: takeFirst(seed.Description, namesgenerator.GetRandomName(1)), - CreatedBy: takeFirst(seed.CreatedBy, uuid.New()), - Icon: takeFirst(seed.Icon, namesgenerator.GetRandomName(1)), + CreatedAt: TakeFirst(seed.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(seed.UpdatedAt, dbtime.Now()), + OrganizationID: TakeFirst(seed.OrganizationID, uuid.New()), + Name: TakeFirst(seed.Name, namesgenerator.GetRandomName(1)), + Provisioner: TakeFirst(seed.Provisioner, database.ProvisionerTypeEcho), + ActiveVersionID: TakeFirst(seed.ActiveVersionID, uuid.New()), + Description: TakeFirst(seed.Description, namesgenerator.GetRandomName(1)), + CreatedBy: TakeFirst(seed.CreatedBy, uuid.New()), + Icon: TakeFirst(seed.Icon, namesgenerator.GetRandomName(1)), UserACL: seed.UserACL, GroupACL: seed.GroupACL, - DisplayName: takeFirst(seed.DisplayName, namesgenerator.GetRandomName(1)), + DisplayName: TakeFirst(seed.DisplayName, namesgenerator.GetRandomName(1)), AllowUserCancelWorkspaceJobs: seed.AllowUserCancelWorkspaceJobs, }) require.NoError(t, err, "insert template") - template, err := db.GetTemplateByID(genCtx, id) + template, err := db.GetTemplateByID(Ctx, id) require.NoError(t, err, "get template") return template } @@ -111,55 +111,55 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey) (key database } } - key, err := db.InsertAPIKey(genCtx, database.InsertAPIKeyParams{ - ID: takeFirst(seed.ID, id), + key, err := db.InsertAPIKey(Ctx, database.InsertAPIKeyParams{ + ID: TakeFirst(seed.ID, id), // 0 defaults to 86400 at the db layer - LifetimeSeconds: takeFirst(seed.LifetimeSeconds, 0), - HashedSecret: takeFirstSlice(seed.HashedSecret, hashed[:]), + LifetimeSeconds: TakeFirst(seed.LifetimeSeconds, 0), + HashedSecret: TakeFirstSlice(seed.HashedSecret, hashed[:]), IPAddress: ip, - UserID: takeFirst(seed.UserID, uuid.New()), - LastUsed: takeFirst(seed.LastUsed, dbtime.Now()), - ExpiresAt: takeFirst(seed.ExpiresAt, dbtime.Now().Add(time.Hour)), - CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(seed.UpdatedAt, dbtime.Now()), - LoginType: takeFirst(seed.LoginType, database.LoginTypePassword), - Scope: takeFirst(seed.Scope, database.APIKeyScopeAll), - TokenName: takeFirst(seed.TokenName), + UserID: TakeFirst(seed.UserID, uuid.New()), + LastUsed: TakeFirst(seed.LastUsed, dbtime.Now()), + ExpiresAt: TakeFirst(seed.ExpiresAt, dbtime.Now().Add(time.Hour)), + CreatedAt: TakeFirst(seed.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(seed.UpdatedAt, dbtime.Now()), + LoginType: TakeFirst(seed.LoginType, database.LoginTypePassword), + Scope: TakeFirst(seed.Scope, database.APIKeyScopeAll), + TokenName: TakeFirst(seed.TokenName), }) require.NoError(t, err, "insert api key") return key, fmt.Sprintf("%s-%s", key.ID, secret) } func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent { - agt, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{ - ID: takeFirst(orig.ID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), - ResourceID: takeFirst(orig.ResourceID, uuid.New()), - AuthToken: takeFirst(orig.AuthToken, uuid.New()), + agt, err := db.InsertWorkspaceAgent(Ctx, database.InsertWorkspaceAgentParams{ + ID: TakeFirst(orig.ID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + ResourceID: TakeFirst(orig.ResourceID, uuid.New()), + AuthToken: TakeFirst(orig.AuthToken, uuid.New()), AuthInstanceID: sql.NullString{ - String: takeFirst(orig.AuthInstanceID.String, namesgenerator.GetRandomName(1)), - Valid: takeFirst(orig.AuthInstanceID.Valid, true), + String: TakeFirst(orig.AuthInstanceID.String, namesgenerator.GetRandomName(1)), + Valid: TakeFirst(orig.AuthInstanceID.Valid, true), }, - Architecture: takeFirst(orig.Architecture, "amd64"), + Architecture: TakeFirst(orig.Architecture, "amd64"), EnvironmentVariables: pqtype.NullRawMessage{ - RawMessage: takeFirstSlice(orig.EnvironmentVariables.RawMessage, []byte("{}")), - Valid: takeFirst(orig.EnvironmentVariables.Valid, false), + RawMessage: TakeFirstSlice(orig.EnvironmentVariables.RawMessage, []byte("{}")), + Valid: TakeFirst(orig.EnvironmentVariables.Valid, false), }, - OperatingSystem: takeFirst(orig.OperatingSystem, "linux"), - Directory: takeFirst(orig.Directory, ""), + OperatingSystem: TakeFirst(orig.OperatingSystem, "linux"), + Directory: TakeFirst(orig.Directory, ""), InstanceMetadata: pqtype.NullRawMessage{ - RawMessage: takeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), - Valid: takeFirst(orig.ResourceMetadata.Valid, false), + RawMessage: TakeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), + Valid: TakeFirst(orig.ResourceMetadata.Valid, false), }, ResourceMetadata: pqtype.NullRawMessage{ - RawMessage: takeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), - Valid: takeFirst(orig.ResourceMetadata.Valid, false), + RawMessage: TakeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), + Valid: TakeFirst(orig.ResourceMetadata.Valid, false), }, - ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600), - TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"), - MOTDFile: takeFirst(orig.TroubleshootingURL, ""), + ConnectionTimeoutSeconds: TakeFirst(orig.ConnectionTimeoutSeconds, 3600), + TroubleshootingURL: TakeFirst(orig.TroubleshootingURL, "https://example.com"), + MOTDFile: TakeFirst(orig.TroubleshootingURL, ""), DisplayApps: append([]database.DisplayApp{}, orig.DisplayApps...), }) require.NoError(t, err, "insert workspace agent") @@ -167,58 +167,58 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen } func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace { - workspace, err := db.InsertWorkspace(genCtx, database.InsertWorkspaceParams{ - ID: takeFirst(orig.ID, uuid.New()), - OwnerID: takeFirst(orig.OwnerID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), - TemplateID: takeFirst(orig.TemplateID, uuid.New()), - LastUsedAt: takeFirst(orig.LastUsedAt, dbtime.Now()), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + workspace, err := db.InsertWorkspace(Ctx, database.InsertWorkspaceParams{ + ID: TakeFirst(orig.ID, uuid.New()), + OwnerID: TakeFirst(orig.OwnerID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), + TemplateID: TakeFirst(orig.TemplateID, uuid.New()), + LastUsedAt: TakeFirst(orig.LastUsedAt, dbtime.Now()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), AutostartSchedule: orig.AutostartSchedule, Ttl: orig.Ttl, - AutomaticUpdates: takeFirst(orig.AutomaticUpdates, database.AutomaticUpdatesNever), + AutomaticUpdates: TakeFirst(orig.AutomaticUpdates, database.AutomaticUpdatesNever), }) require.NoError(t, err, "insert workspace") return workspace } func WorkspaceAgentLogSource(t testing.TB, db database.Store, orig database.WorkspaceAgentLogSource) database.WorkspaceAgentLogSource { - sources, err := db.InsertWorkspaceAgentLogSources(genCtx, database.InsertWorkspaceAgentLogSourcesParams{ - WorkspaceAgentID: takeFirst(orig.WorkspaceAgentID, uuid.New()), - ID: []uuid.UUID{takeFirst(orig.ID, uuid.New())}, - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - DisplayName: []string{takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1))}, - Icon: []string{takeFirst(orig.Icon, namesgenerator.GetRandomName(1))}, + sources, err := db.InsertWorkspaceAgentLogSources(Ctx, database.InsertWorkspaceAgentLogSourcesParams{ + WorkspaceAgentID: TakeFirst(orig.WorkspaceAgentID, uuid.New()), + ID: []uuid.UUID{TakeFirst(orig.ID, uuid.New())}, + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + DisplayName: []string{TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1))}, + Icon: []string{TakeFirst(orig.Icon, namesgenerator.GetRandomName(1))}, }) require.NoError(t, err, "insert workspace agent log source") return sources[0] } func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuild { - buildID := takeFirst(orig.ID, uuid.New()) + buildID := TakeFirst(orig.ID, uuid.New()) var build database.WorkspaceBuild err := db.InTx(func(db database.Store) error { - err := db.InsertWorkspaceBuild(genCtx, database.InsertWorkspaceBuildParams{ + err := db.InsertWorkspaceBuild(Ctx, database.InsertWorkspaceBuildParams{ ID: buildID, - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.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, dbtime.Now().Add(time.Hour)), - MaxDeadline: takeFirst(orig.MaxDeadline, time.Time{}), - Reason: takeFirst(orig.Reason, database.BuildReasonInitiator), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.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, dbtime.Now().Add(time.Hour)), + MaxDeadline: TakeFirst(orig.MaxDeadline, time.Time{}), + Reason: TakeFirst(orig.Reason, database.BuildReasonInitiator), }) if err != nil { return err } - build, err = db.GetWorkspaceBuildByID(genCtx, buildID) + build, err = db.GetWorkspaceBuildByID(Ctx, buildID) if err != nil { return err } @@ -229,28 +229,63 @@ func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuil return build } +func WorkspaceBuildParameters(t testing.TB, db database.Store, orig []database.WorkspaceBuildParameter) []database.WorkspaceBuildParameter { + if len(orig) == 0 { + return nil + } + + var ( + names = make([]string, 0, len(orig)) + values = make([]string, 0, len(orig)) + params []database.WorkspaceBuildParameter + ) + for _, param := range orig { + names = append(names, param.Name) + values = append(values, param.Value) + } + err := db.InTx(func(tx database.Store) error { + id := TakeFirst(orig[0].WorkspaceBuildID, uuid.New()) + err := tx.InsertWorkspaceBuildParameters(Ctx, database.InsertWorkspaceBuildParametersParams{ + WorkspaceBuildID: id, + Name: names, + Value: values, + }) + if err != nil { + return err + } + + params, err = tx.GetWorkspaceBuildParameters(Ctx, id) + if err != nil { + return err + } + return err + }, nil) + require.NoError(t, err) + return params +} + func User(t testing.TB, db database.Store, orig database.User) database.User { - user, err := db.InsertUser(genCtx, database.InsertUserParams{ - ID: takeFirst(orig.ID, uuid.New()), - Email: takeFirst(orig.Email, namesgenerator.GetRandomName(1)), - Username: takeFirst(orig.Username, namesgenerator.GetRandomName(1)), - HashedPassword: takeFirstSlice(orig.HashedPassword, []byte(must(cryptorand.String(32)))), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - RBACRoles: takeFirstSlice(orig.RBACRoles, []string{}), - LoginType: takeFirst(orig.LoginType, database.LoginTypePassword), + user, err := db.InsertUser(Ctx, database.InsertUserParams{ + ID: TakeFirst(orig.ID, uuid.New()), + Email: TakeFirst(orig.Email, namesgenerator.GetRandomName(1)), + Username: TakeFirst(orig.Username, namesgenerator.GetRandomName(1)), + HashedPassword: TakeFirstSlice(orig.HashedPassword, []byte(must(cryptorand.String(32)))), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + RBACRoles: TakeFirstSlice(orig.RBACRoles, []string{}), + LoginType: TakeFirst(orig.LoginType, database.LoginTypePassword), }) require.NoError(t, err, "insert user") - user, err = db.UpdateUserStatus(genCtx, database.UpdateUserStatusParams{ + user, err = db.UpdateUserStatus(Ctx, database.UpdateUserStatusParams{ ID: user.ID, - Status: takeFirst(orig.Status, database.UserStatusActive), + Status: TakeFirst(orig.Status, database.UserStatusActive), UpdatedAt: dbtime.Now(), }) require.NoError(t, err, "insert user") if !orig.LastSeenAt.IsZero() { - user, err = db.UpdateUserLastSeenAt(genCtx, database.UpdateUserLastSeenAtParams{ + user, err = db.UpdateUserLastSeenAt(Ctx, database.UpdateUserLastSeenAtParams{ ID: user.ID, LastSeenAt: orig.LastSeenAt, UpdatedAt: user.UpdatedAt, @@ -259,7 +294,7 @@ func User(t testing.TB, db database.Store, orig database.User) database.User { } if orig.Deleted { - err = db.UpdateUserDeletedByID(genCtx, database.UpdateUserDeletedByIDParams{ + err = db.UpdateUserDeletedByID(Ctx, database.UpdateUserDeletedByIDParams{ ID: user.ID, Deleted: orig.Deleted, }) @@ -269,50 +304,50 @@ func User(t testing.TB, db database.Store, orig database.User) database.User { } func GitSSHKey(t testing.TB, db database.Store, orig database.GitSSHKey) database.GitSSHKey { - key, err := db.InsertGitSSHKey(genCtx, database.InsertGitSSHKeyParams{ - UserID: takeFirst(orig.UserID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - PrivateKey: takeFirst(orig.PrivateKey, ""), - PublicKey: takeFirst(orig.PublicKey, ""), + key, err := db.InsertGitSSHKey(Ctx, database.InsertGitSSHKeyParams{ + UserID: TakeFirst(orig.UserID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + PrivateKey: TakeFirst(orig.PrivateKey, ""), + PublicKey: TakeFirst(orig.PublicKey, ""), }) require.NoError(t, err, "insert ssh key") return key } func Organization(t testing.TB, db database.Store, orig database.Organization) database.Organization { - org, err := db.InsertOrganization(genCtx, database.InsertOrganizationParams{ - ID: takeFirst(orig.ID, uuid.New()), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Description: takeFirst(orig.Description, namesgenerator.GetRandomName(1)), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + org, err := db.InsertOrganization(Ctx, database.InsertOrganizationParams{ + ID: TakeFirst(orig.ID, uuid.New()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Description: TakeFirst(orig.Description, namesgenerator.GetRandomName(1)), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), }) require.NoError(t, err, "insert organization") return org } func OrganizationMember(t testing.TB, db database.Store, orig database.OrganizationMember) database.OrganizationMember { - mem, err := db.InsertOrganizationMember(genCtx, database.InsertOrganizationMemberParams{ - OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), - UserID: takeFirst(orig.UserID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - Roles: takeFirstSlice(orig.Roles, []string{}), + mem, err := db.InsertOrganizationMember(Ctx, database.InsertOrganizationMemberParams{ + OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), + UserID: TakeFirst(orig.UserID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + Roles: TakeFirstSlice(orig.Roles, []string{}), }) require.NoError(t, err, "insert organization") return mem } func Group(t testing.TB, db database.Store, orig database.Group) database.Group { - name := takeFirst(orig.Name, namesgenerator.GetRandomName(1)) - group, err := db.InsertGroup(genCtx, database.InsertGroupParams{ - ID: takeFirst(orig.ID, uuid.New()), + name := TakeFirst(orig.Name, namesgenerator.GetRandomName(1)) + group, err := db.InsertGroup(Ctx, database.InsertGroupParams{ + ID: TakeFirst(orig.ID, uuid.New()), Name: name, - DisplayName: takeFirst(orig.DisplayName, name), - OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), - AvatarURL: takeFirst(orig.AvatarURL, "https://logo.example.com"), - QuotaAllowance: takeFirst(orig.QuotaAllowance, 0), + DisplayName: TakeFirst(orig.DisplayName, name), + OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), + AvatarURL: TakeFirst(orig.AvatarURL, "https://logo.example.com"), + QuotaAllowance: TakeFirst(orig.QuotaAllowance, 0), }) require.NoError(t, err, "insert group") return group @@ -320,11 +355,11 @@ func Group(t testing.TB, db database.Store, orig database.Group) database.Group func GroupMember(t testing.TB, db database.Store, orig database.GroupMember) database.GroupMember { member := database.GroupMember{ - UserID: takeFirst(orig.UserID, uuid.New()), - GroupID: takeFirst(orig.GroupID, uuid.New()), + UserID: TakeFirst(orig.UserID, uuid.New()), + GroupID: TakeFirst(orig.GroupID, uuid.New()), } //nolint:gosimple - err := db.InsertGroupMember(genCtx, database.InsertGroupMemberParams{ + err := db.InsertGroupMember(Ctx, database.InsertGroupMemberParams{ UserID: member.UserID, GroupID: member.GroupID, }) @@ -335,7 +370,7 @@ func GroupMember(t testing.TB, db database.Store, orig database.GroupMember) dat // ProvisionerJob is a bit more involved to get the values such as "completedAt", "startedAt", "cancelledAt" set. ps // can be set to nil if you are SURE that you don't require a provisionerdaemon to acquire the job in your test. func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig database.ProvisionerJob) database.ProvisionerJob { - jobID := takeFirst(orig.ID, uuid.New()) + jobID := TakeFirst(orig.ID, uuid.New()) // Always set some tags to prevent Acquire from grabbing jobs it should not. if !orig.StartedAt.Time.IsZero() { if orig.Tags == nil { @@ -345,17 +380,17 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data orig.Tags[jobID.String()] = "true" } - job, err := db.InsertProvisionerJob(genCtx, database.InsertProvisionerJobParams{ + job, err := db.InsertProvisionerJob(Ctx, database.InsertProvisionerJobParams{ ID: jobID, - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), - InitiatorID: takeFirst(orig.InitiatorID, uuid.New()), - Provisioner: takeFirst(orig.Provisioner, database.ProvisionerTypeEcho), - StorageMethod: takeFirst(orig.StorageMethod, database.ProvisionerStorageMethodFile), - FileID: takeFirst(orig.FileID, uuid.New()), - Type: takeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild), - Input: takeFirstSlice(orig.Input, []byte("{}")), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), + InitiatorID: TakeFirst(orig.InitiatorID, uuid.New()), + Provisioner: TakeFirst(orig.Provisioner, database.ProvisionerTypeEcho), + StorageMethod: TakeFirst(orig.StorageMethod, database.ProvisionerStorageMethodFile), + FileID: TakeFirst(orig.FileID, uuid.New()), + Type: TakeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild), + Input: TakeFirstSlice(orig.Input, []byte("{}")), Tags: orig.Tags, TraceMetadata: pqtype.NullRawMessage{}, }) @@ -365,7 +400,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data require.NoError(t, err, "post job to pubsub") } if !orig.StartedAt.Time.IsZero() { - job, err = db.AcquireProvisionerJob(genCtx, database.AcquireProvisionerJobParams{ + job, err = db.AcquireProvisionerJob(Ctx, database.AcquireProvisionerJobParams{ StartedAt: orig.StartedAt, Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, Tags: must(json.Marshal(orig.Tags)), @@ -377,7 +412,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data } if !orig.CompletedAt.Time.IsZero() || orig.Error.String != "" { - err := db.UpdateProvisionerJobWithCompleteByID(genCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ + err := db.UpdateProvisionerJobWithCompleteByID(Ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: jobID, UpdatedAt: job.UpdatedAt, CompletedAt: orig.CompletedAt, @@ -387,7 +422,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data require.NoError(t, err) } if !orig.CanceledAt.Time.IsZero() { - err := db.UpdateProvisionerJobWithCancelByID(genCtx, database.UpdateProvisionerJobWithCancelByIDParams{ + err := db.UpdateProvisionerJobWithCancelByID(Ctx, database.UpdateProvisionerJobWithCancelByIDParams{ ID: jobID, CanceledAt: orig.CanceledAt, CompletedAt: orig.CompletedAt, @@ -395,66 +430,66 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data require.NoError(t, err) } - job, err = db.GetProvisionerJobByID(genCtx, jobID) + job, err = db.GetProvisionerJobByID(Ctx, jobID) require.NoError(t, err) return job } func WorkspaceApp(t testing.TB, db database.Store, orig database.WorkspaceApp) database.WorkspaceApp { - resource, err := db.InsertWorkspaceApp(genCtx, database.InsertWorkspaceAppParams{ - ID: takeFirst(orig.ID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - AgentID: takeFirst(orig.AgentID, uuid.New()), - Slug: takeFirst(orig.Slug, namesgenerator.GetRandomName(1)), - DisplayName: takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), - Icon: takeFirst(orig.Icon, namesgenerator.GetRandomName(1)), + resource, err := db.InsertWorkspaceApp(Ctx, database.InsertWorkspaceAppParams{ + ID: TakeFirst(orig.ID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + AgentID: TakeFirst(orig.AgentID, uuid.New()), + Slug: TakeFirst(orig.Slug, namesgenerator.GetRandomName(1)), + DisplayName: TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), + Icon: TakeFirst(orig.Icon, namesgenerator.GetRandomName(1)), Command: sql.NullString{ - String: takeFirst(orig.Command.String, "ls"), + String: TakeFirst(orig.Command.String, "ls"), Valid: orig.Command.Valid, }, Url: sql.NullString{ - String: takeFirst(orig.Url.String), + String: TakeFirst(orig.Url.String), Valid: orig.Url.Valid, }, External: orig.External, Subdomain: orig.Subdomain, - SharingLevel: takeFirst(orig.SharingLevel, database.AppSharingLevelOwner), - HealthcheckUrl: takeFirst(orig.HealthcheckUrl, "https://localhost:8000"), - HealthcheckInterval: takeFirst(orig.HealthcheckInterval, 60), - HealthcheckThreshold: takeFirst(orig.HealthcheckThreshold, 60), - Health: takeFirst(orig.Health, database.WorkspaceAppHealthHealthy), + SharingLevel: TakeFirst(orig.SharingLevel, database.AppSharingLevelOwner), + HealthcheckUrl: TakeFirst(orig.HealthcheckUrl, "https://localhost:8000"), + HealthcheckInterval: TakeFirst(orig.HealthcheckInterval, 60), + HealthcheckThreshold: TakeFirst(orig.HealthcheckThreshold, 60), + Health: TakeFirst(orig.Health, database.WorkspaceAppHealthHealthy), }) require.NoError(t, err, "insert app") return resource } func WorkspaceResource(t testing.TB, db database.Store, orig database.WorkspaceResource) database.WorkspaceResource { - resource, err := db.InsertWorkspaceResource(genCtx, database.InsertWorkspaceResourceParams{ - ID: takeFirst(orig.ID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - JobID: takeFirst(orig.JobID, uuid.New()), - Transition: takeFirst(orig.Transition, database.WorkspaceTransitionStart), - Type: takeFirst(orig.Type, "fake_resource"), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Hide: takeFirst(orig.Hide, false), - Icon: takeFirst(orig.Icon, ""), + resource, err := db.InsertWorkspaceResource(Ctx, database.InsertWorkspaceResourceParams{ + ID: TakeFirst(orig.ID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + JobID: TakeFirst(orig.JobID, uuid.New()), + Transition: TakeFirst(orig.Transition, database.WorkspaceTransitionStart), + Type: TakeFirst(orig.Type, "fake_resource"), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Hide: TakeFirst(orig.Hide, false), + Icon: TakeFirst(orig.Icon, ""), InstanceType: sql.NullString{ - String: takeFirst(orig.InstanceType.String, ""), - Valid: takeFirst(orig.InstanceType.Valid, false), + String: TakeFirst(orig.InstanceType.String, ""), + Valid: TakeFirst(orig.InstanceType.Valid, false), }, - DailyCost: takeFirst(orig.DailyCost, 0), + DailyCost: TakeFirst(orig.DailyCost, 0), }) require.NoError(t, err, "insert resource") return resource } func WorkspaceResourceMetadatums(t testing.TB, db database.Store, seed database.WorkspaceResourceMetadatum) []database.WorkspaceResourceMetadatum { - meta, err := db.InsertWorkspaceResourceMetadata(genCtx, database.InsertWorkspaceResourceMetadataParams{ - WorkspaceResourceID: takeFirst(seed.WorkspaceResourceID, uuid.New()), - Key: []string{takeFirst(seed.Key, namesgenerator.GetRandomName(1))}, - Value: []string{takeFirst(seed.Value.String, namesgenerator.GetRandomName(1))}, - Sensitive: []bool{takeFirst(seed.Sensitive, false)}, + meta, err := db.InsertWorkspaceResourceMetadata(Ctx, database.InsertWorkspaceResourceMetadataParams{ + WorkspaceResourceID: TakeFirst(seed.WorkspaceResourceID, uuid.New()), + Key: []string{TakeFirst(seed.Key, namesgenerator.GetRandomName(1))}, + Value: []string{TakeFirst(seed.Value.String, namesgenerator.GetRandomName(1))}, + Sensitive: []bool{TakeFirst(seed.Sensitive, false)}, }) require.NoError(t, err, "insert meta data") return meta @@ -465,22 +500,22 @@ func WorkspaceProxy(t testing.TB, db database.Store, orig database.WorkspaceProx require.NoError(t, err, "generate secret") hashedSecret := sha256.Sum256([]byte(secret)) - proxy, err := db.InsertWorkspaceProxy(genCtx, database.InsertWorkspaceProxyParams{ - ID: takeFirst(orig.ID, uuid.New()), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), - DisplayName: takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), - Icon: takeFirst(orig.Icon, namesgenerator.GetRandomName(1)), + proxy, err := db.InsertWorkspaceProxy(Ctx, database.InsertWorkspaceProxyParams{ + ID: TakeFirst(orig.ID, uuid.New()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + DisplayName: TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), + Icon: TakeFirst(orig.Icon, namesgenerator.GetRandomName(1)), TokenHashedSecret: hashedSecret[:], - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - DerpEnabled: takeFirst(orig.DerpEnabled, false), - DerpOnly: takeFirst(orig.DerpEnabled, false), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + DerpEnabled: TakeFirst(orig.DerpEnabled, false), + DerpOnly: TakeFirst(orig.DerpEnabled, false), }) require.NoError(t, err, "insert proxy") // Also set these fields if the caller wants them. if orig.Url != "" || orig.WildcardHostname != "" { - proxy, err = db.RegisterWorkspaceProxy(genCtx, database.RegisterWorkspaceProxyParams{ + proxy, err = db.RegisterWorkspaceProxy(Ctx, database.RegisterWorkspaceProxyParams{ Url: orig.Url, WildcardHostname: orig.WildcardHostname, ID: proxy.ID, @@ -491,28 +526,28 @@ func WorkspaceProxy(t testing.TB, db database.Store, orig database.WorkspaceProx } func File(t testing.TB, db database.Store, orig database.File) database.File { - file, err := db.InsertFile(genCtx, database.InsertFileParams{ - ID: takeFirst(orig.ID, uuid.New()), - Hash: takeFirst(orig.Hash, hex.EncodeToString(make([]byte, 32))), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - CreatedBy: takeFirst(orig.CreatedBy, uuid.New()), - Mimetype: takeFirst(orig.Mimetype, "application/x-tar"), - Data: takeFirstSlice(orig.Data, []byte{}), + file, err := db.InsertFile(Ctx, database.InsertFileParams{ + ID: TakeFirst(orig.ID, uuid.New()), + Hash: TakeFirst(orig.Hash, hex.EncodeToString(make([]byte, 32))), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + CreatedBy: TakeFirst(orig.CreatedBy, uuid.New()), + Mimetype: TakeFirst(orig.Mimetype, "application/x-tar"), + Data: TakeFirstSlice(orig.Data, []byte{}), }) require.NoError(t, err, "insert file") return file } func UserLink(t testing.TB, db database.Store, orig database.UserLink) database.UserLink { - link, err := db.InsertUserLink(genCtx, database.InsertUserLinkParams{ - UserID: takeFirst(orig.UserID, uuid.New()), - LoginType: takeFirst(orig.LoginType, database.LoginTypeGithub), - LinkedID: takeFirst(orig.LinkedID), - OAuthAccessToken: takeFirst(orig.OAuthAccessToken, uuid.NewString()), - OAuthAccessTokenKeyID: takeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), - OAuthRefreshToken: takeFirst(orig.OAuthRefreshToken, uuid.NewString()), - OAuthRefreshTokenKeyID: takeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), - OAuthExpiry: takeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), + link, err := db.InsertUserLink(Ctx, database.InsertUserLinkParams{ + UserID: TakeFirst(orig.UserID, uuid.New()), + LoginType: TakeFirst(orig.LoginType, database.LoginTypeGithub), + LinkedID: TakeFirst(orig.LinkedID), + OAuthAccessToken: TakeFirst(orig.OAuthAccessToken, uuid.NewString()), + OAuthAccessTokenKeyID: TakeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), + OAuthRefreshToken: TakeFirst(orig.OAuthRefreshToken, uuid.NewString()), + OAuthRefreshTokenKeyID: TakeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), + OAuthExpiry: TakeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), }) require.NoError(t, err, "insert link") @@ -520,17 +555,17 @@ func UserLink(t testing.TB, db database.Store, orig database.UserLink) database. } func ExternalAuthLink(t testing.TB, db database.Store, orig database.ExternalAuthLink) database.ExternalAuthLink { - msg := takeFirst(&orig.OAuthExtra, &pqtype.NullRawMessage{}) - link, err := db.InsertExternalAuthLink(genCtx, database.InsertExternalAuthLinkParams{ - ProviderID: takeFirst(orig.ProviderID, uuid.New().String()), - UserID: takeFirst(orig.UserID, uuid.New()), - OAuthAccessToken: takeFirst(orig.OAuthAccessToken, uuid.NewString()), - OAuthAccessTokenKeyID: takeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), - OAuthRefreshToken: takeFirst(orig.OAuthRefreshToken, uuid.NewString()), - OAuthRefreshTokenKeyID: takeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), - OAuthExpiry: takeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + msg := TakeFirst(&orig.OAuthExtra, &pqtype.NullRawMessage{}) + link, err := db.InsertExternalAuthLink(Ctx, database.InsertExternalAuthLinkParams{ + ProviderID: TakeFirst(orig.ProviderID, uuid.New().String()), + UserID: TakeFirst(orig.UserID, uuid.New()), + OAuthAccessToken: TakeFirst(orig.OAuthAccessToken, uuid.NewString()), + OAuthAccessTokenKeyID: TakeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), + OAuthRefreshToken: TakeFirst(orig.OAuthRefreshToken, uuid.NewString()), + OAuthRefreshTokenKeyID: TakeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), + OAuthExpiry: TakeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), OAuthExtra: *msg, }) @@ -541,24 +576,24 @@ func ExternalAuthLink(t testing.TB, db database.Store, orig database.ExternalAut func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVersion) database.TemplateVersion { var version database.TemplateVersion err := db.InTx(func(db database.Store) error { - versionID := takeFirst(orig.ID, uuid.New()) - err := db.InsertTemplateVersion(genCtx, database.InsertTemplateVersionParams{ + versionID := TakeFirst(orig.ID, uuid.New()) + err := db.InsertTemplateVersion(Ctx, database.InsertTemplateVersionParams{ ID: versionID, TemplateID: orig.TemplateID, - OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), Message: orig.Message, - Readme: takeFirst(orig.Readme, namesgenerator.GetRandomName(1)), - JobID: takeFirst(orig.JobID, uuid.New()), - CreatedBy: takeFirst(orig.CreatedBy, uuid.New()), + Readme: TakeFirst(orig.Readme, namesgenerator.GetRandomName(1)), + JobID: TakeFirst(orig.JobID, uuid.New()), + CreatedBy: TakeFirst(orig.CreatedBy, uuid.New()), }) if err != nil { return err } - version, err = db.GetTemplateVersionByID(genCtx, versionID) + version, err = db.GetTemplateVersionByID(Ctx, versionID) if err != nil { return err } @@ -570,42 +605,66 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers } func TemplateVersionVariable(t testing.TB, db database.Store, orig database.TemplateVersionVariable) database.TemplateVersionVariable { - version, err := db.InsertTemplateVersionVariable(genCtx, database.InsertTemplateVersionVariableParams{ - TemplateVersionID: takeFirst(orig.TemplateVersionID, uuid.New()), - Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Description: takeFirst(orig.Description, namesgenerator.GetRandomName(1)), - Type: takeFirst(orig.Type, "string"), - Value: takeFirst(orig.Value, ""), - DefaultValue: takeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), - Required: takeFirst(orig.Required, false), - Sensitive: takeFirst(orig.Sensitive, false), + version, err := db.InsertTemplateVersionVariable(Ctx, database.InsertTemplateVersionVariableParams{ + TemplateVersionID: TakeFirst(orig.TemplateVersionID, uuid.New()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Description: TakeFirst(orig.Description, namesgenerator.GetRandomName(1)), + Type: TakeFirst(orig.Type, "string"), + Value: TakeFirst(orig.Value, ""), + DefaultValue: TakeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), + Required: TakeFirst(orig.Required, false), + Sensitive: TakeFirst(orig.Sensitive, false), }) require.NoError(t, err, "insert template version variable") return version } +func TemplateVersionParameter(t testing.TB, db database.Store, orig database.TemplateVersionParameter) database.TemplateVersionParameter { + version, err := db.InsertTemplateVersionParameter(Ctx, database.InsertTemplateVersionParameterParams{ + TemplateVersionID: TakeFirst(orig.TemplateVersionID, uuid.New()), + Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Description: TakeFirst(orig.Description, namesgenerator.GetRandomName(1)), + Type: TakeFirst(orig.Type, "string"), + Mutable: TakeFirst(orig.Mutable, false), + DefaultValue: TakeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), + Icon: TakeFirst(orig.Icon, namesgenerator.GetRandomName(1)), + Options: TakeFirstSlice(orig.Options, []byte("[]")), + ValidationRegex: TakeFirst(orig.ValidationRegex, ""), + ValidationMin: TakeFirst(orig.ValidationMin, sql.NullInt32{}), + ValidationMax: TakeFirst(orig.ValidationMax, sql.NullInt32{}), + ValidationError: TakeFirst(orig.ValidationError, ""), + ValidationMonotonic: TakeFirst(orig.ValidationMonotonic, ""), + Required: TakeFirst(orig.Required, false), + DisplayName: TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), + DisplayOrder: TakeFirst(orig.DisplayOrder, 0), + Ephemeral: TakeFirst(orig.Ephemeral, false), + }) + require.NoError(t, err, "insert template version parameter") + return version +} + func WorkspaceAgentStat(t testing.TB, db database.Store, orig database.WorkspaceAgentStat) database.WorkspaceAgentStat { if orig.ConnectionsByProto == nil { orig.ConnectionsByProto = json.RawMessage([]byte("{}")) } - scheme, err := db.InsertWorkspaceAgentStat(genCtx, database.InsertWorkspaceAgentStatParams{ - ID: takeFirst(orig.ID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UserID: takeFirst(orig.UserID, uuid.New()), - TemplateID: takeFirst(orig.TemplateID, uuid.New()), - WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()), - AgentID: takeFirst(orig.AgentID, uuid.New()), + scheme, err := db.InsertWorkspaceAgentStat(Ctx, database.InsertWorkspaceAgentStatParams{ + ID: TakeFirst(orig.ID, uuid.New()), + CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), + UserID: TakeFirst(orig.UserID, uuid.New()), + TemplateID: TakeFirst(orig.TemplateID, uuid.New()), + WorkspaceID: TakeFirst(orig.WorkspaceID, uuid.New()), + AgentID: TakeFirst(orig.AgentID, uuid.New()), ConnectionsByProto: orig.ConnectionsByProto, - ConnectionCount: takeFirst(orig.ConnectionCount, 0), - RxPackets: takeFirst(orig.RxPackets, 0), - RxBytes: takeFirst(orig.RxBytes, 0), - TxPackets: takeFirst(orig.TxPackets, 0), - TxBytes: takeFirst(orig.TxBytes, 0), - SessionCountVSCode: takeFirst(orig.SessionCountVSCode, 0), - SessionCountJetBrains: takeFirst(orig.SessionCountJetBrains, 0), - SessionCountReconnectingPTY: takeFirst(orig.SessionCountReconnectingPTY, 0), - SessionCountSSH: takeFirst(orig.SessionCountSSH, 0), - ConnectionMedianLatencyMS: takeFirst(orig.ConnectionMedianLatencyMS, 0), + ConnectionCount: TakeFirst(orig.ConnectionCount, 0), + RxPackets: TakeFirst(orig.RxPackets, 0), + RxBytes: TakeFirst(orig.RxBytes, 0), + TxPackets: TakeFirst(orig.TxPackets, 0), + TxBytes: TakeFirst(orig.TxBytes, 0), + SessionCountVSCode: TakeFirst(orig.SessionCountVSCode, 0), + SessionCountJetBrains: TakeFirst(orig.SessionCountJetBrains, 0), + SessionCountReconnectingPTY: TakeFirst(orig.SessionCountReconnectingPTY, 0), + SessionCountSSH: TakeFirst(orig.SessionCountSSH, 0), + ConnectionMedianLatencyMS: TakeFirst(orig.ConnectionMedianLatencyMS, 0), }) require.NoError(t, err, "insert workspace agent stat") return scheme @@ -624,9 +683,9 @@ func takeFirstIP(values ...net.IPNet) net.IPNet { }) } -// takeFirstSlice implements takeFirst for []any. +// TakeFirstSlice implements takeFirst for []any. // []any is not a comparable type. -func takeFirstSlice[T any](values ...[]T) []T { +func TakeFirstSlice[T any](values ...[]T) []T { return takeFirstF(values, func(v []T) bool { return len(v) != 0 }) @@ -647,8 +706,8 @@ func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { return empty } -// takeFirst will take the first non-empty value. -func takeFirst[Value comparable](values ...Value) Value { +// TakeFirst will take the first non-empty value. +func TakeFirst[Value comparable](values ...Value) Value { var empty Value return takeFirstF(values, func(v Value) bool { return v != empty diff --git a/coderd/database/dbgen/dbgen_test.go b/coderd/database/dbgen/dbgen_test.go index 531c8bb25cd53..eaf5a0e764482 100644 --- a/coderd/database/dbgen/dbgen_test.go +++ b/coderd/database/dbgen/dbgen_test.go @@ -173,6 +173,22 @@ func TestGenerator(t *testing.T) { exp := dbgen.GitSSHKey(t, db, database.GitSSHKey{}) require.Equal(t, exp, must(db.GetGitSSHKey(context.Background(), exp.UserID))) }) + + t.Run("WorkspaceBuildParameters", func(t *testing.T) { + t.Parallel() + db := dbmem.New() + exp := dbgen.WorkspaceBuildParameters(t, db, []database.WorkspaceBuildParameter{{}, {}, {}}) + require.Equal(t, exp, must(db.GetWorkspaceBuildParameters(context.Background(), exp[0].WorkspaceBuildID))) + }) + + t.Run("TemplateVersionParameter", func(t *testing.T) { + t.Parallel() + db := dbmem.New() + exp := dbgen.TemplateVersionParameter(t, db, database.TemplateVersionParameter{}) + actual := must(db.GetTemplateVersionParameters(context.Background(), exp.TemplateVersionID)) + require.Len(t, actual, 1) + require.Equal(t, exp, actual[0]) + }) } func must[T any](value T, err error) T { diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 002c8a7c00d8c..769c66fadf270 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -23,6 +23,7 @@ import ( "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" + "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/database/dbgen" "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/database/dbtime" @@ -343,94 +344,84 @@ func TestWorkspace(t *testing.T) { func TestResolveAutostart(t *testing.T) { t.Parallel() - t.Run("OK", func(t *testing.T) { - t.Parallel() - ownerClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - owner := coderdtest.CreateFirstUser(t, ownerClient) - version1 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil) - coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version1.ID) - template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, version1.ID) + ownerClient, db := coderdtest.NewWithDatabase(t, nil) + owner := coderdtest.CreateFirstUser(t, ownerClient) - params := &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ - { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Parameters: []*proto.RichParameter{ - { - Name: "param", - Description: "param", - Required: true, - Mutable: true, - }, - }, - }, - }, - }, - }, - ProvisionApply: echo.ApplyComplete, - } - version2 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, params, func(ctvr *codersdk.CreateTemplateVersionRequest) { - ctvr.TemplateID = template.ID - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version2.ID) + template, version1 := dbfake.TemplateWithVersion(t, db, database.Template{ + CreatedBy: owner.UserID, + OrganizationID: owner.OrganizationID, + }, database.TemplateVersion{}, database.ProvisionerJob{}) + + params := []database.TemplateVersionParameter{{ + Name: "param", + DefaultValue: "", + Required: true, + }} + version2, _ := dbfake.TemplateVersionWithParams(t, db, database.TemplateVersion{ + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + OrganizationID: template.OrganizationID, + CreatedBy: owner.UserID, + }, database.ProvisionerJob{}, []database.TemplateVersionParameter{{ + Name: "param", + Required: true, + }}) - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() - client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) - workspace := coderdtest.CreateWorkspace(t, client, owner.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) { - cwr.AutomaticUpdates = codersdk.AutomaticUpdatesAlways - }) - coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) + client, member := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) + workspace := dbfake.Workspace(t, db, database.Workspace{ + TemplateID: template.ID, + OwnerID: member.ID, + OrganizationID: owner.OrganizationID, + AutomaticUpdates: database.AutomaticUpdatesAlways, + }) + _ = dbfake.WorkspaceBuild(t, db, workspace, database.WorkspaceBuild{ + TemplateVersionID: version1.ID, + }) - err := ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: version2.ID, - }) - require.NoError(t, err) + err := ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: version2.ID, + }) + require.NoError(t, err) - // Autostart shouldn't be possible if parameters do not match. - resp, err := client.ResolveAutostart(ctx, workspace.ID.String()) - require.NoError(t, err) - require.True(t, resp.ParameterMismatch) - - update, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{ - TemplateVersionID: version2.ID, - Transition: codersdk.WorkspaceTransitionStart, - RichParameterValues: []codersdk.WorkspaceBuildParameter{ - { - Name: "param", - Value: "Hello", - }, - }, - }) - require.NoError(t, err) - coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, update.ID) + // Autostart shouldn't be possible if parameters do not match. + resp, err := client.ResolveAutostart(ctx, workspace.ID.String()) + require.NoError(t, err) + require.True(t, resp.ParameterMismatch) - // We should be able to autostart since parameters are updated. - resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) - require.NoError(t, err) - require.False(t, resp.ParameterMismatch) + _, _ = dbfake.WorkspaceBuildWithParameters(t, db, workspace, database.WorkspaceBuild{ + TemplateVersionID: version2.ID, + BuildNumber: 2, + }, []database.WorkspaceBuildParameter{ + { + Name: "param", + Value: "hello", + }, + }) + require.NoError(t, err) - // Create one last version where the parameters are the same as the previous - // version. - version3 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, params, func(ctvr *codersdk.CreateTemplateVersionRequest) { - ctvr.TemplateID = template.ID - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version3.ID) + // We should be able to autostart since parameters are updated. + resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) + require.NoError(t, err) + require.False(t, resp.ParameterMismatch) - err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: version3.ID, - }) - require.NoError(t, err) + version3, _ := dbfake.TemplateVersionWithParams(t, db, database.TemplateVersion{ + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + OrganizationID: owner.OrganizationID, + CreatedBy: owner.UserID, + }, database.ProvisionerJob{}, params) - // Even though we're out of date we should still be able to autostart - // since parameters resolve. - resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) - require.NoError(t, err) - require.False(t, resp.ParameterMismatch) + err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: version3.ID, }) + require.NoError(t, err) + + // Even though we're out of date we should still be able to autostart + // since parameters resolve. + resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) + require.NoError(t, err) + require.False(t, resp.ParameterMismatch) } func TestAdminViewAllWorkspaces(t *testing.T) { diff --git a/enterprise/coderd/coderdenttest/coderdenttest.go b/enterprise/coderd/coderdenttest/coderdenttest.go index 26e3bfaef22d6..3ec1f87a6da43 100644 --- a/enterprise/coderd/coderdenttest/coderdenttest.go +++ b/enterprise/coderd/coderdenttest/coderdenttest.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbmem" "github.com/coder/coder/v2/coderd/database/pubsub" @@ -67,6 +68,11 @@ func New(t *testing.T, options *Options) (*codersdk.Client, codersdk.CreateFirst return client, user } +func NewWithDatabase(t *testing.T, options *Options) (*codersdk.Client, database.Store, codersdk.CreateFirstUserResponse) { + client, _, api, user := NewWithAPI(t, options) + return client, api.Database, user +} + func NewWithAPI(t *testing.T, options *Options) ( *codersdk.Client, io.Closer, *coderd.API, codersdk.CreateFirstUserResponse, ) { diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 09ba533b483db..06e8dea4d1828 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/require" "cdr.dev/slog/sloggers/slogtest" @@ -16,6 +17,7 @@ import ( "github.com/coder/coder/v2/coderd/autobuild" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/rbac" agplschedule "github.com/coder/coder/v2/coderd/schedule" "github.com/coder/coder/v2/coderd/schedule/cron" @@ -25,7 +27,6 @@ import ( "github.com/coder/coder/v2/enterprise/coderd/license" "github.com/coder/coder/v2/enterprise/coderd/schedule" "github.com/coder/coder/v2/provisioner/echo" - "github.com/coder/coder/v2/provisionersdk/proto" "github.com/coder/coder/v2/testutil" ) @@ -1074,7 +1075,7 @@ func TestWorkspaceLock(t *testing.T) { func TestResolveAutostart(t *testing.T) { t.Parallel() - ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{ + ownerClient, db, owner := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ IncludeProvisionerDaemon: true, TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{}, @@ -1086,52 +1087,45 @@ func TestResolveAutostart(t *testing.T) { }, }) - version1 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil) - coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version1.ID) - template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, version1.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.RequireActiveVersion = true - }) - - params := &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ - { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Parameters: []*proto.RichParameter{ - { - Name: "param", - Description: "param", - Required: true, - Mutable: true, - }, - }, - }, - }, - }, - }, - ProvisionApply: echo.ApplyComplete, - } - version2 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, params, func(ctvr *codersdk.CreateTemplateVersionRequest) { - ctvr.TemplateID = template.ID - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version2.ID) + template, version1 := dbfake.TemplateWithVersion(t, db, database.Template{ + CreatedBy: owner.UserID, + OrganizationID: owner.OrganizationID, + }, database.TemplateVersion{}, database.ProvisionerJob{}) ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) - workspace := coderdtest.CreateWorkspace(t, client, owner.OrganizationID, template.ID) - coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) + _, err := ownerClient.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ + RequireActiveVersion: true, + }) + require.NoError(t, err) + + version2, _ := dbfake.TemplateVersionWithParams(t, db, database.TemplateVersion{ + TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, + OrganizationID: template.OrganizationID, + CreatedBy: owner.UserID, + }, database.ProvisionerJob{}, []database.TemplateVersionParameter{{ + Name: "param", + Required: true, + }}) + + client, member := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) + workspace := dbfake.Workspace(t, db, database.Workspace{ + TemplateID: template.ID, + OwnerID: member.ID, + OrganizationID: owner.OrganizationID, + AutomaticUpdates: database.AutomaticUpdatesNever, + }) + _ = dbfake.WorkspaceBuild(t, db, workspace, database.WorkspaceBuild{ + TemplateVersionID: version1.ID, + }) - //nolint:gocritic - err := ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ + err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ ID: version2.ID, }) require.NoError(t, err) - // Autostart shouldn't be possible since the template requires automatic - // updates. + // Autostart shouldn't be possible if parameters do not match. resp, err := client.ResolveAutostart(ctx, workspace.ID.String()) require.NoError(t, err) require.True(t, resp.ParameterMismatch) From 3ea291167d95b50d00179058fc9acc06435b769e Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Thu, 9 Nov 2023 08:39:31 +0000 Subject: [PATCH 2/9] lint --- coderd/database/dbfake/dbfake.go | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 010332128a979..3f16f7675bcf3 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -16,7 +16,6 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/provisionerdserver" "github.com/coder/coder/v2/coderd/telemetry" - "github.com/coder/coder/v2/provisionersdk/proto" sdkproto "github.com/coder/coder/v2/provisionersdk/proto" ) @@ -38,7 +37,7 @@ func Workspace(t testing.TB, db database.Store, seed database.Workspace) databas return dbgen.Workspace(t, db, seed) } -func TemplateWithVersion(t testing.TB, db database.Store, tpl database.Template, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*proto.Resource) (database.Template, database.TemplateVersion) { +func TemplateWithVersion(t testing.TB, db database.Store, tpl database.Template, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*sdkproto.Resource) (database.Template, database.TemplateVersion) { t.Helper() template := dbgen.Template(t, db, tpl) @@ -58,7 +57,7 @@ func TemplateWithVersion(t testing.TB, db database.Store, tpl database.Template, return template, version } -func TemplateVersion(t testing.TB, db database.Store, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*proto.Resource) database.TemplateVersion { +func TemplateVersion(t testing.TB, db database.Store, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*sdkproto.Resource) database.TemplateVersion { templateVersion := dbgen.TemplateVersion(t, db, tv) payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{ TemplateVersionID: templateVersion.ID, @@ -199,25 +198,3 @@ func ProvisionerJobResources(t testing.TB, db database.Store, job uuid.UUID, tra require.NoError(t, err) } } - -func must[V any](v V, err error) V { - if err != nil { - panic(err) - } - return v -} - -// takeFirstF takes the first value that returns true -func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { - for _, v := range values { - if take(v) { - return v - } - } - // If all empty, return the last element - if len(values) > 0 { - return values[len(values)-1] - } - var empty Value - return empty -} From 03ef4a6b0465085d85635617eb57cf8e797d07c1 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 28 Nov 2023 21:40:35 +0000 Subject: [PATCH 3/9] refactor db fake --- coderd/database/dbfake/dbfake.go | 250 ++++++++++++++++----------- coderd/database/dbgen/dbgen.go | 6 +- coderd/workspaces_test.go | 67 +++---- enterprise/coderd/workspaces_test.go | 45 ++--- 4 files changed, 200 insertions(+), 168 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 683c0ed492ed3..c3e9a8f29924b 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -16,11 +16,19 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/database/pubsub" "github.com/coder/coder/v2/coderd/provisionerdserver" + "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/codersdk" sdkproto "github.com/coder/coder/v2/provisionersdk/proto" ) +var sysCtx = dbauthz.As(context.Background(), rbac.Subject{ + ID: "owner", + Roles: rbac.Roles(must(rbac.RoleNames{rbac.RoleOwner()}.Expand())), + Groups: []string{}, + Scope: rbac.ExpandableScope(rbac.ScopeAll), +}) + type WorkspaceBuilder struct { t testing.TB db database.Store @@ -30,10 +38,10 @@ type WorkspaceBuilder struct { } type WorkspaceResponse struct { - Workspace database.Workspace - Template database.Template - Build database.WorkspaceBuild - AgentToken string + Workspace database.Workspace + TemplateVersion database.TemplateVersion + Build database.WorkspaceBuild + AgentToken string } func NewWorkspaceBuilder(t testing.TB, db database.Store) WorkspaceBuilder { @@ -67,86 +75,34 @@ func (b WorkspaceBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkp } func (b WorkspaceBuilder) Do() WorkspaceResponse { + b.t.Helper() + var r WorkspaceResponse + // This intentionally fulfills the minimum requirements of the schema. // Tests can provide a custom template ID if necessary. if b.seed.TemplateID == uuid.Nil { - r.Template = dbgen.Template(b.t, b.db, database.Template{ + version := TemplateVersion(b.t, b.db).Seed(database.TemplateVersion{ OrganizationID: b.seed.OrganizationID, CreatedBy: b.seed.OwnerID, - }) - b.seed.TemplateID = r.Template.ID - b.seed.OwnerID = r.Template.CreatedBy - b.seed.OrganizationID = r.Template.OrganizationID + }).Do() + + b.seed.TemplateID = version.TemplateID.UUID + r.TemplateVersion = version } r.Workspace = dbgen.Workspace(b.t, b.db, b.seed) if b.agentToken != "" { r.AgentToken = b.agentToken r.Build = NewWorkspaceBuildBuilder(b.t, b.db, r.Workspace). + Seed(database.WorkspaceBuild{ + TemplateVersionID: r.TemplateVersion.ID, + }). Resource(b.resources...). Do() } return r } -func TemplateWithVersion(t testing.TB, db database.Store, tpl database.Template, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*sdkproto.Resource) (database.Template, database.TemplateVersion) { - t.Helper() - - template := dbgen.Template(t, db, tpl) - - tv.TemplateID = dbgen.TakeFirst(tv.TemplateID, uuid.NullUUID{UUID: template.ID, Valid: true}) - tv.OrganizationID = dbgen.TakeFirst(tv.OrganizationID, template.OrganizationID) - tv.CreatedBy = dbgen.TakeFirst(tv.CreatedBy, template.CreatedBy) - version := TemplateVersion(t, db, tv, job, resources...) - - err := db.UpdateTemplateActiveVersionByID(dbgen.Ctx, database.UpdateTemplateActiveVersionByIDParams{ - ID: template.ID, - ActiveVersionID: version.ID, - UpdatedAt: dbtime.Now(), - }) - require.NoError(t, err) - - return template, version -} - -func TemplateVersion(t testing.TB, db database.Store, tv database.TemplateVersion, job database.ProvisionerJob, resources ...*sdkproto.Resource) database.TemplateVersion { - templateVersion := dbgen.TemplateVersion(t, db, tv) - payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{ - TemplateVersionID: templateVersion.ID, - }) - require.NoError(t, err) - - job.ID = dbgen.TakeFirst(job.ID, templateVersion.JobID) - job.OrganizationID = dbgen.TakeFirst(job.OrganizationID, templateVersion.OrganizationID) - job.Input = dbgen.TakeFirstSlice(job.Input, payload) - job.Type = dbgen.TakeFirst(job.Type, database.ProvisionerJobTypeTemplateVersionImport) - job.CompletedAt = dbgen.TakeFirst(job.CompletedAt, sql.NullTime{ - Time: dbtime.Now(), - Valid: true, - }) - - job = dbgen.ProvisionerJob(t, db, nil, job) - ProvisionerJobResources(t, db, job.ID, "", resources...) - return templateVersion -} - -func TemplateVersionWithParams(t testing.TB, db database.Store, tv database.TemplateVersion, job database.ProvisionerJob, params []database.TemplateVersionParameter) (database.TemplateVersion, []database.TemplateVersionParameter) { - t.Helper() - - version := TemplateVersion(t, db, tv, job) - tvps := make([]database.TemplateVersionParameter, 0, len(params)) - - for _, param := range params { - if param.TemplateVersionID == uuid.Nil { - param.TemplateVersionID = version.ID - } - tvp := dbgen.TemplateVersionParameter(t, db, param) - tvps = append(tvps, tvp) - } - - return version, tvps -} - type WorkspaceBuildBuilder struct { t testing.TB db database.Store @@ -154,6 +110,7 @@ type WorkspaceBuildBuilder struct { ws database.Workspace seed database.WorkspaceBuild resources []*sdkproto.Resource + params []database.WorkspaceBuildParameter } func NewWorkspaceBuildBuilder(t testing.TB, db database.Store, ws database.Workspace) WorkspaceBuildBuilder { @@ -161,7 +118,7 @@ func NewWorkspaceBuildBuilder(t testing.TB, db database.Store, ws database.Works } func (b WorkspaceBuildBuilder) Pubsub(ps pubsub.Pubsub) WorkspaceBuildBuilder { - //nolint: revive // returns modified struct + // nolint: revive // returns modified struct b.ps = ps return b } @@ -178,6 +135,11 @@ func (b WorkspaceBuildBuilder) Resource(resource ...*sdkproto.Resource) Workspac return b } +func (b WorkspaceBuildBuilder) Params(params ...database.WorkspaceBuildParameter) WorkspaceBuildBuilder { + b.params = params + return b +} + func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { b.t.Helper() jobID := uuid.New() @@ -190,9 +152,7 @@ func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { WorkspaceBuildID: b.seed.ID, }) require.NoError(b.t, err) - //nolint:gocritic // This is only used by tests. - ctx := dbauthz.AsSystemRestricted(context.Background()) - job, err := b.db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{ + job, err := b.db.InsertProvisionerJob(sysCtx, database.InsertProvisionerJobParams{ ID: jobID, CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), @@ -207,7 +167,7 @@ func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { TraceMetadata: pqtype.NullRawMessage{}, }) require.NoError(b.t, err, "insert job") - err = b.db.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ + err = b.db.UpdateProvisionerJobWithCompleteByID(sysCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: job.ID, UpdatedAt: dbtime.Now(), Error: sql.NullString{}, @@ -222,38 +182,24 @@ func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { // This intentionally fulfills the minimum requirements of the schema. // Tests can provide a custom version ID if necessary. if b.seed.TemplateVersionID == uuid.Nil { - jobID := uuid.New() - templateVersion := dbgen.TemplateVersion(b.t, b.db, database.TemplateVersion{ - JobID: jobID, - OrganizationID: b.ws.OrganizationID, - CreatedBy: b.ws.OwnerID, - TemplateID: uuid.NullUUID{ - UUID: b.ws.TemplateID, - Valid: true, - }, - }) - payload, _ := json.Marshal(provisionerdserver.TemplateVersionImportJob{ - TemplateVersionID: templateVersion.ID, - }) - dbgen.ProvisionerJob(b.t, b.db, nil, database.ProvisionerJob{ - ID: jobID, - OrganizationID: b.ws.OrganizationID, - Input: payload, - Type: database.ProvisionerJobTypeTemplateVersionImport, - CompletedAt: sql.NullTime{ - Time: dbtime.Now(), - Valid: true, - }, - }) - NewProvisionerJobResourcesBuilder(b.t, b.db, jobID, b.seed.Transition, b.resources...).Do() - b.seed.TemplateVersionID = templateVersion.ID + version := TemplateVersion(b.t, b.db). + Resources(b.resources...). + Pubsub(b.ps). + Do() + b.seed.TemplateVersionID = version.ID } + build := dbgen.WorkspaceBuild(b.t, b.db, b.seed) NewProvisionerJobResourcesBuilder(b.t, b.db, job.ID, b.seed.Transition, b.resources...).Do() if b.ps != nil { err = b.ps.Publish(codersdk.WorkspaceNotifyChannel(build.WorkspaceID), []byte{}) require.NoError(b.t, err) } + + for i := range b.params { + b.params[i].WorkspaceBuildID = build.ID + } + _ = dbgen.WorkspaceBuildParameters(b.t, b.db, b.params) return build } @@ -287,7 +233,115 @@ func (b ProvisionerJobResourcesBuilder) Do() { } for _, resource := range b.resources { //nolint:gocritic // This is only used by tests. - err := provisionerdserver.InsertWorkspaceResource(dbauthz.AsSystemRestricted(context.Background()), b.db, b.jobID, transition, resource, &telemetry.Snapshot{}) + err := provisionerdserver.InsertWorkspaceResource(sysCtx, b.db, b.jobID, transition, resource, &telemetry.Snapshot{}) require.NoError(b.t, err) } } + +type TemplateVersionBuilder struct { + t testing.TB + db database.Store + seed database.TemplateVersion + ps pubsub.Pubsub + resources []*sdkproto.Resource + params []database.TemplateVersionParameter + template database.Template + promote bool +} + +func TemplateVersion(t testing.TB, db database.Store) TemplateVersionBuilder { + return TemplateVersionBuilder{ + t: t, + db: db, + promote: true, + } +} + +func (t TemplateVersionBuilder) Seed(v database.TemplateVersion) TemplateVersionBuilder { + // nolint: revive // returns modified struct + t.seed = v + return t +} + +func (t TemplateVersionBuilder) Pubsub(ps pubsub.Pubsub) TemplateVersionBuilder { + // nolint: revive // returns modified struct + t.ps = ps + return t +} + +func (t TemplateVersionBuilder) Resources(rs ...*sdkproto.Resource) TemplateVersionBuilder { + // nolint: revive // returns modified struct + t.resources = rs + return t +} + +func (t TemplateVersionBuilder) Params(ps ...database.TemplateVersionParameter) TemplateVersionBuilder { + // nolint: revive // returns modified struct + t.params = ps + return t +} + +func (t TemplateVersionBuilder) Do() database.TemplateVersion { + t.t.Helper() + + t.seed.OrganizationID = dbgen.TakeFirst(t.seed.OrganizationID, uuid.New()) + t.seed.ID = dbgen.TakeFirst(t.seed.ID, uuid.New()) + t.seed.CreatedBy = dbgen.TakeFirst(t.seed.CreatedBy, uuid.New()) + + if t.seed.TemplateID.UUID == uuid.Nil { + template := dbgen.Template(t.t, t.db, database.Template{ + ActiveVersionID: t.seed.ID, + OrganizationID: t.seed.OrganizationID, + CreatedBy: t.seed.CreatedBy, + }) + t.seed.TemplateID = uuid.NullUUID{ + Valid: true, + UUID: template.ID, + } + } + + version := dbgen.TemplateVersion(t.t, t.db, t.seed) + + if t.template.ID != uuid.Nil { + // Always make this version the active version. We can easily + // add a conditional to the builder to opt out of this when + // necessary. + err := t.db.UpdateTemplateActiveVersionByID(sysCtx, database.UpdateTemplateActiveVersionByIDParams{ + ID: t.template.ID, + ActiveVersionID: t.seed.ID, + UpdatedAt: dbtime.Now(), + }) + require.NoError(t.t, err) + } + + payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{ + TemplateVersionID: t.seed.ID, + }) + require.NoError(t.t, err) + + job := dbgen.ProvisionerJob(t.t, t.db, t.ps, database.ProvisionerJob{ + ID: version.JobID, + OrganizationID: t.seed.OrganizationID, + InitiatorID: t.seed.CreatedBy, + Type: database.ProvisionerJobTypeTemplateVersionImport, + Input: payload, + }) + + t.seed.JobID = job.ID + + NewProvisionerJobResourcesBuilder(t.t, t.db, job.ID, "", t.resources...).Do() + + for i, param := range t.params { + param.TemplateVersionID = version.ID + t.params[i] = dbgen.TemplateVersionParameter(t.t, t.db, param) + } + + return version +} + +func must[V any](v V, err error) V { + if err != nil { + panic(err) + } + return v +} diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 417789fe11122..1269e211cf9ac 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -370,6 +370,8 @@ func GroupMember(t testing.TB, db database.Store, orig database.GroupMember) dat // ProvisionerJob is a bit more involved to get the values such as "completedAt", "startedAt", "cancelledAt" set. ps // can be set to nil if you are SURE that you don't require a provisionerdaemon to acquire the job in your test. func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig database.ProvisionerJob) database.ProvisionerJob { + t.Helper() + jobID := TakeFirst(orig.ID, uuid.New()) // Always set some tags to prevent Acquire from grabbing jobs it should not. if !orig.StartedAt.Time.IsZero() { @@ -580,7 +582,7 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers versionID := TakeFirst(orig.ID, uuid.New()) err := db.InsertTemplateVersion(Ctx, database.InsertTemplateVersionParams{ ID: versionID, - TemplateID: orig.TemplateID, + TemplateID: TakeFirst(orig.TemplateID, uuid.NullUUID{}), OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), @@ -621,6 +623,8 @@ func TemplateVersionVariable(t testing.TB, db database.Store, orig database.Temp } func TemplateVersionParameter(t testing.TB, db database.Store, orig database.TemplateVersionParameter) database.TemplateVersionParameter { + t.Helper() + version, err := db.InsertTemplateVersionParameter(Ctx, database.InsertTemplateVersionParameterParams{ TemplateVersionID: TakeFirst(orig.TemplateVersionID, uuid.New()), Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 769c66fadf270..81e4d552e3c1f 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -347,75 +347,56 @@ func TestResolveAutostart(t *testing.T) { ownerClient, db := coderdtest.NewWithDatabase(t, nil) owner := coderdtest.CreateFirstUser(t, ownerClient) - template, version1 := dbfake.TemplateWithVersion(t, db, database.Template{ - CreatedBy: owner.UserID, - OrganizationID: owner.OrganizationID, - }, database.TemplateVersion{}, database.ProvisionerJob{}) - - params := []database.TemplateVersionParameter{{ + param := database.TemplateVersionParameter{ Name: "param", DefaultValue: "", Required: true, - }} - version2, _ := dbfake.TemplateVersionWithParams(t, db, database.TemplateVersion{ - TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, - OrganizationID: template.OrganizationID, - CreatedBy: owner.UserID, - }, database.ProvisionerJob{}, []database.TemplateVersionParameter{{ - Name: "param", - Required: true, - }}) + } ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() client, member := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) - workspace := dbfake.Workspace(t, db, database.Workspace{ - TemplateID: template.ID, + build := dbfake.NewWorkspaceBuilder(t, db).Seed(database.Workspace{ OwnerID: member.ID, OrganizationID: owner.OrganizationID, AutomaticUpdates: database.AutomaticUpdatesAlways, - }) - _ = dbfake.WorkspaceBuild(t, db, workspace, database.WorkspaceBuild{ - TemplateVersionID: version1.ID, - }) + }).WithAgent().Do() - err := ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: version2.ID, - }) - require.NoError(t, err) + workspace := build.Workspace + version1 := build.TemplateVersion + + version2 := dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{ + CreatedBy: owner.UserID, + OrganizationID: owner.OrganizationID, + TemplateID: uuid.NullUUID{UUID: version1.TemplateID.UUID, Valid: true}, + }).Params(param).Do() // Autostart shouldn't be possible if parameters do not match. resp, err := client.ResolveAutostart(ctx, workspace.ID.String()) require.NoError(t, err) require.True(t, resp.ParameterMismatch) - _, _ = dbfake.WorkspaceBuildWithParameters(t, db, workspace, database.WorkspaceBuild{ - TemplateVersionID: version2.ID, + _ = dbfake.NewWorkspaceBuildBuilder(t, db, workspace).Seed(database.WorkspaceBuild{ BuildNumber: 2, - }, []database.WorkspaceBuildParameter{ - { - Name: "param", - Value: "hello", - }, - }) - require.NoError(t, err) + TemplateVersionID: version2.ID, + }).Params(database.WorkspaceBuildParameter{ + Name: "param", + Value: "hello", + }).Do() // We should be able to autostart since parameters are updated. resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) require.NoError(t, err) require.False(t, resp.ParameterMismatch) - version3, _ := dbfake.TemplateVersionWithParams(t, db, database.TemplateVersion{ - TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, - OrganizationID: owner.OrganizationID, + // Create another version that has the same parameters as version2. + // We should be able to update without issue. + _ = dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{ CreatedBy: owner.UserID, - }, database.ProvisionerJob{}, params) - - err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: version3.ID, - }) - require.NoError(t, err) + OrganizationID: owner.OrganizationID, + TemplateID: uuid.NullUUID{UUID: version1.TemplateID.UUID, Valid: true}, + }).Params(param).Do() // Even though we're out of date we should still be able to autostart // since parameters resolve. diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index b0cd16a3a2b84..cb5273ec28abd 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -1158,8 +1158,7 @@ func TestResolveAutostart(t *testing.T) { ownerClient, db, owner := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ Options: &coderdtest.Options{ - IncludeProvisionerDaemon: true, - TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{}, + TemplateScheduleStore: &schedule.EnterpriseTemplateScheduleStore{}, }, LicenseOptions: &coderdenttest.LicenseOptions{ Features: license.Features{ @@ -1168,43 +1167,37 @@ func TestResolveAutostart(t *testing.T) { }, }) - template, version1 := dbfake.TemplateWithVersion(t, db, database.Template{ - CreatedBy: owner.UserID, - OrganizationID: owner.OrganizationID, - }, database.TemplateVersion{}, database.ProvisionerJob{}) + version1 := dbfake.TemplateVersion(t, db). + Seed(database.TemplateVersion{ + CreatedBy: owner.UserID, + OrganizationID: owner.OrganizationID, + }).Do() ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - _, err := ownerClient.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ + _, err := ownerClient.UpdateTemplateMeta(ctx, version1.TemplateID.UUID, codersdk.UpdateTemplateMeta{ RequireActiveVersion: true, }) require.NoError(t, err) - version2, _ := dbfake.TemplateVersionWithParams(t, db, database.TemplateVersion{ - TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true}, - OrganizationID: template.OrganizationID, - CreatedBy: owner.UserID, - }, database.ProvisionerJob{}, []database.TemplateVersionParameter{{ - Name: "param", - Required: true, - }}) - client, member := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) - workspace := dbfake.Workspace(t, db, database.Workspace{ - TemplateID: template.ID, + + workspace := dbfake.NewWorkspaceBuilder(t, db).Seed(database.Workspace{ + TemplateID: version1.TemplateID.UUID, OwnerID: member.ID, OrganizationID: owner.OrganizationID, AutomaticUpdates: database.AutomaticUpdatesNever, - }) - _ = dbfake.WorkspaceBuild(t, db, workspace, database.WorkspaceBuild{ - TemplateVersionID: version1.ID, - }) + }).WithAgent().Do().Workspace - err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: version2.ID, - }) - require.NoError(t, err) + _ = dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{ + CreatedBy: owner.UserID, + OrganizationID: owner.OrganizationID, + TemplateID: uuid.NullUUID{UUID: version1.TemplateID.UUID, Valid: true}, + }).Params(database.TemplateVersionParameter{ + Name: "param", + Required: true, + }).Do() // Autostart shouldn't be possible if parameters do not match. resp, err := client.ResolveAutostart(ctx, workspace.ID.String()) From 7cc8f4c828b5bb4319539a306c79622dd9382d75 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Wed, 29 Nov 2023 23:12:35 +0000 Subject: [PATCH 4/9] finish up api --- coderd/coderdtest/coderdtest.go | 2 + coderd/database/dbfake/dbfake.go | 267 ++++++++++++++++----------- coderd/database/dbgen/dbgen.go | 4 + coderd/workspaces_test.go | 54 +++--- enterprise/coderd/workspaces_test.go | 18 +- 5 files changed, 209 insertions(+), 136 deletions(-) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index c5bb986c0c8d9..f5ed26cfe97d6 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -753,6 +753,8 @@ func CreateWorkspaceBuild( transition database.WorkspaceTransition, mutators ...func(*codersdk.CreateWorkspaceBuildRequest), ) codersdk.WorkspaceBuild { + t.Helper() + req := codersdk.CreateWorkspaceBuildRequest{ Transition: codersdk.WorkspaceTransition(transition), } diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index aed35cac4eebd..eb020f77d0b45 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -29,32 +29,52 @@ var sysCtx = dbauthz.As(context.Background(), rbac.Subject{ Scope: rbac.ExpandableScope(rbac.ScopeAll), }) -type WorkspaceBuilder struct { +type WorkspaceResponse struct { + Workspace database.Workspace + Build database.WorkspaceBuild + AgentToken string + TemplateVersionResponse +} + +type WorkspaceBuildBuilder struct { t testing.TB db database.Store - seed database.Workspace + ps pubsub.Pubsub + ws database.Workspace + seed database.WorkspaceBuild resources []*sdkproto.Resource + params []database.WorkspaceBuildParameter agentToken string } -type WorkspaceResponse struct { - Workspace database.Workspace - TemplateVersion database.TemplateVersion - Build database.WorkspaceBuild - AgentToken string +func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace) WorkspaceBuildBuilder { + return WorkspaceBuildBuilder{t: t, db: db, ws: ws} } -func Workspace(t testing.TB, db database.Store) WorkspaceBuilder { - return WorkspaceBuilder{t: t, db: db} +func (b WorkspaceBuildBuilder) Pubsub(ps pubsub.Pubsub) WorkspaceBuildBuilder { + // nolint: revive // returns modified struct + b.ps = ps + return b } -func (b WorkspaceBuilder) Seed(seed database.Workspace) WorkspaceBuilder { +func (b WorkspaceBuildBuilder) Seed(seed database.WorkspaceBuild) WorkspaceBuildBuilder { //nolint: revive // returns modified struct b.seed = seed return b } -func (b WorkspaceBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkproto.Agent) WorkspaceBuilder { +func (b WorkspaceBuildBuilder) Resource(resource ...*sdkproto.Resource) WorkspaceBuildBuilder { + //nolint: revive // returns modified struct + b.resources = append(b.resources, resource...) + return b +} + +func (b WorkspaceBuildBuilder) Params(params ...database.WorkspaceBuildParameter) WorkspaceBuildBuilder { + b.params = params + return b +} + +func (b WorkspaceBuildBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkproto.Agent) WorkspaceBuildBuilder { //nolint: revive // returns modified struct b.agentToken = uuid.NewString() agents := []*sdkproto.Agent{{ @@ -74,74 +94,39 @@ func (b WorkspaceBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkp return b } -func (b WorkspaceBuilder) Do() WorkspaceResponse { +func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { b.t.Helper() + jobID := uuid.New() + b.seed.ID = uuid.New() + b.seed.JobID = jobID - var r WorkspaceResponse - - // This intentionally fulfills the minimum requirements of the schema. - // Tests can provide a custom template ID if necessary. - if b.seed.TemplateID == uuid.Nil { - version := TemplateVersion(b.t, b.db).Seed(database.TemplateVersion{ - OrganizationID: b.seed.OrganizationID, - CreatedBy: b.seed.OwnerID, - }).Do() - - b.seed.TemplateID = version.TemplateID.UUID - r.TemplateVersion = version - } - r.Workspace = dbgen.Workspace(b.t, b.db, b.seed) - if b.agentToken != "" { - r.AgentToken = b.agentToken - r.Build = WorkspaceBuild(b.t, b.db, r.Workspace). - Resource(b.resources...). + var resp WorkspaceResponse + if b.ws.TemplateID == uuid.Nil { + resp.TemplateVersionResponse = TemplateVersion(b.t, b.db). + Resources(b.resources...). + Pubsub(b.ps). + Seed(database.TemplateVersion{ + OrganizationID: b.ws.OrganizationID, + CreatedBy: b.ws.OwnerID, + }). Do() + b.ws.TemplateID = resp.Template.ID + b.seed.TemplateVersionID = resp.TemplateVersion.ID } - return r -} - -type WorkspaceBuildBuilder struct { - t testing.TB - db database.Store - ps pubsub.Pubsub - ws database.Workspace - seed database.WorkspaceBuild - resources []*sdkproto.Resource - params []database.WorkspaceBuildParameter -} - -func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace) WorkspaceBuildBuilder { - return WorkspaceBuildBuilder{t: t, db: db, ws: ws} -} - -func (b WorkspaceBuildBuilder) Pubsub(ps pubsub.Pubsub) WorkspaceBuildBuilder { - // nolint: revive // returns modified struct - b.ps = ps - return b -} - -func (b WorkspaceBuildBuilder) Seed(seed database.WorkspaceBuild) WorkspaceBuildBuilder { - //nolint: revive // returns modified struct - b.seed = seed - return b -} - -func (b WorkspaceBuildBuilder) Resource(resource ...*sdkproto.Resource) WorkspaceBuildBuilder { - //nolint: revive // returns modified struct - b.resources = append(b.resources, resource...) - return b -} -func (b WorkspaceBuildBuilder) Params(params ...database.WorkspaceBuildParameter) WorkspaceBuildBuilder { - b.params = params - return b -} + // If no template version is set assume the active version. + if b.seed.TemplateVersionID == uuid.Nil { + template, err := b.db.GetTemplateByID(sysCtx, b.ws.TemplateID) + require.NoError(b.t, err) + require.NotNil(b.t, template.ActiveVersionID, "active version ID unexpectedly nil") + b.seed.TemplateVersionID = template.ActiveVersionID + } -func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { - b.t.Helper() - jobID := uuid.New() - b.seed.ID = uuid.New() - b.seed.JobID = jobID + // No ID on the workspace implies we should generate an entry. + if b.ws.ID == uuid.Nil { + b.ws = dbgen.Workspace(b.t, b.db, b.ws) + resp.Workspace = b.ws + } b.seed.WorkspaceID = b.ws.ID // Create a provisioner job for the build! @@ -149,6 +134,7 @@ func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { WorkspaceBuildID: b.seed.ID, }) require.NoError(b.t, err) + job, err := b.db.InsertProvisionerJob(sysCtx, database.InsertProvisionerJobParams{ ID: jobID, CreatedAt: dbtime.Now(), @@ -164,6 +150,7 @@ func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { TraceMetadata: pqtype.NullRawMessage{}, }) require.NoError(b.t, err, "insert job") + err = b.db.UpdateProvisionerJobWithCompleteByID(sysCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: job.ID, UpdatedAt: dbtime.Now(), @@ -176,28 +163,18 @@ func (b WorkspaceBuildBuilder) Do() database.WorkspaceBuild { }) require.NoError(b.t, err, "complete job") - // This intentionally fulfills the minimum requirements of the schema. - // Tests can provide a custom version ID if necessary. - if b.seed.TemplateVersionID == uuid.Nil { - version := TemplateVersion(b.t, b.db). - Resources(b.resources...). - Pubsub(b.ps). - Do() - b.seed.TemplateVersionID = version.ID - } - - build := dbgen.WorkspaceBuild(b.t, b.db, b.seed) + resp.Build = dbgen.WorkspaceBuild(b.t, b.db, b.seed) ProvisionerJobResources(b.t, b.db, job.ID, b.seed.Transition, b.resources...).Do() if b.ps != nil { - err = b.ps.Publish(codersdk.WorkspaceNotifyChannel(build.WorkspaceID), []byte{}) + err = b.ps.Publish(codersdk.WorkspaceNotifyChannel(resp.Build.WorkspaceID), []byte{}) require.NoError(b.t, err) } for i := range b.params { - b.params[i].WorkspaceBuildID = build.ID + b.params[i].WorkspaceBuildID = resp.Build.ID } _ = dbgen.WorkspaceBuildParameters(b.t, b.db, b.params) - return build + return resp } type ProvisionerJobResourcesBuilder struct { @@ -235,6 +212,11 @@ func (b ProvisionerJobResourcesBuilder) Do() { } } +type TemplateVersionResponse struct { + Template database.Template + TemplateVersion database.TemplateVersion +} + type TemplateVersionBuilder struct { t testing.TB db database.Store @@ -242,7 +224,6 @@ type TemplateVersionBuilder struct { ps pubsub.Pubsub resources []*sdkproto.Resource params []database.TemplateVersionParameter - template database.Template promote bool } @@ -278,38 +259,37 @@ func (t TemplateVersionBuilder) Params(ps ...database.TemplateVersionParameter) return t } -func (t TemplateVersionBuilder) Do() database.TemplateVersion { +func (t TemplateVersionBuilder) Do() TemplateVersionResponse { t.t.Helper() t.seed.OrganizationID = dbgen.TakeFirst(t.seed.OrganizationID, uuid.New()) t.seed.ID = dbgen.TakeFirst(t.seed.ID, uuid.New()) t.seed.CreatedBy = dbgen.TakeFirst(t.seed.CreatedBy, uuid.New()) + var resp TemplateVersionResponse if t.seed.TemplateID.UUID == uuid.Nil { - template := dbgen.Template(t.t, t.db, database.Template{ + resp.Template = dbgen.Template(t.t, t.db, database.Template{ ActiveVersionID: t.seed.ID, OrganizationID: t.seed.OrganizationID, CreatedBy: t.seed.CreatedBy, }) t.seed.TemplateID = uuid.NullUUID{ Valid: true, - UUID: template.ID, + UUID: resp.Template.ID, } } version := dbgen.TemplateVersion(t.t, t.db, t.seed) - if t.template.ID != uuid.Nil { - // Always make this version the active version. We can easily - // add a conditional to the builder to opt out of this when - // necessary. - err := t.db.UpdateTemplateActiveVersionByID(sysCtx, database.UpdateTemplateActiveVersionByIDParams{ - ID: t.template.ID, - ActiveVersionID: t.seed.ID, - UpdatedAt: dbtime.Now(), - }) - require.NoError(t.t, err) - } + // Always make this version the active version. We can easily + // add a conditional to the builder to opt out of this when + // necessary. + err := t.db.UpdateTemplateActiveVersionByID(sysCtx, database.UpdateTemplateActiveVersionByIDParams{ + ID: t.seed.TemplateID.UUID, + ActiveVersionID: t.seed.ID, + UpdatedAt: dbtime.Now(), + }) + require.NoError(t.t, err) payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{ TemplateVersionID: t.seed.ID, @@ -322,6 +302,10 @@ func (t TemplateVersionBuilder) Do() database.TemplateVersion { InitiatorID: t.seed.CreatedBy, Type: database.ProvisionerJobTypeTemplateVersionImport, Input: payload, + CompletedAt: sql.NullTime{ + Time: dbtime.Now(), + Valid: true, + }, }) t.seed.JobID = job.ID @@ -333,7 +317,8 @@ func (t TemplateVersionBuilder) Do() database.TemplateVersion { t.params[i] = dbgen.TemplateVersionParameter(t.t, t.db, param) } - return version + resp.TemplateVersion = version + return resp } func must[V any](v V, err error) V { @@ -342,3 +327,79 @@ func must[V any](v V, err error) V { } return v } + +// TODO update existing callers to call WorkspaceBuildBuilder then delete below. +type WorkspaceBuilder struct { + t testing.TB + db database.Store + seed database.Workspace + resources []*sdkproto.Resource + agentToken string +} + +func Workspace(t testing.TB, db database.Store) WorkspaceBuilder { + return WorkspaceBuilder{t: t, db: db} +} + +func (b WorkspaceBuilder) Seed(seed database.Workspace) WorkspaceBuilder { + //nolint: revive // returns modified struct + b.seed = seed + return b +} + +func (b WorkspaceBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkproto.Agent) WorkspaceBuilder { + //nolint: revive // returns modified struct + b.agentToken = uuid.NewString() + agents := []*sdkproto.Agent{{ + Id: uuid.NewString(), + Auth: &sdkproto.Agent_Token{ + Token: b.agentToken, + }, + }} + for _, m := range mutations { + agents = m(agents) + } + b.resources = append(b.resources, &sdkproto.Resource{ + Name: "example", + Type: "aws_instance", + Agents: agents, + }) + return b +} + +func (b WorkspaceBuilder) Do() WorkspaceResponse { + b.t.Helper() + + var r WorkspaceResponse + + // This intentionally fulfills the minimum requirements of the schema. + // Tests can provide a custom template ID if necessary. + var versionID uuid.UUID + if b.seed.TemplateID == uuid.Nil { + resp := TemplateVersion(b.t, b.db).Seed(database.TemplateVersion{ + OrganizationID: b.seed.OrganizationID, + CreatedBy: b.seed.OwnerID, + }).Do() + + b.seed.TemplateID = resp.Template.ID + r.TemplateVersionResponse = resp + versionID = resp.TemplateVersion.ID + } else { + template, err := b.db.GetTemplateByID(sysCtx, b.seed.TemplateID) + require.NoError(b.t, err) + require.NotNil(b.t, template.ActiveVersionID, "active version unexpectedly nil") + versionID = template.ActiveVersionID + } + + r.Workspace = dbgen.Workspace(b.t, b.db, b.seed) + if b.agentToken != "" { + r.AgentToken = b.agentToken + r.Build = WorkspaceBuild(b.t, b.db, r.Workspace). + Resource(b.resources...). + Seed(database.WorkspaceBuild{ + TemplateVersionID: versionID, + }). + Do().Build + } + return r +} diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 1269e211cf9ac..af9ad0fa4272f 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -167,6 +167,8 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen } func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace { + t.Helper() + workspace, err := db.InsertWorkspace(Ctx, database.InsertWorkspaceParams{ ID: TakeFirst(orig.ID, uuid.New()), OwnerID: TakeFirst(orig.OwnerID, uuid.New()), @@ -197,6 +199,8 @@ func WorkspaceAgentLogSource(t testing.TB, db database.Store, orig database.Work } func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuild { + t.Helper() + buildID := TakeFirst(orig.ID, uuid.New()) var build database.WorkspaceBuild err := db.InTx(func(db database.Store) error { diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 81e4d552e3c1f..71feaf5fc36c8 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -357,52 +357,58 @@ func TestResolveAutostart(t *testing.T) { defer cancel() client, member := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) - build := dbfake.NewWorkspaceBuilder(t, db).Seed(database.Workspace{ + resp := dbfake.WorkspaceBuild(t, db, database.Workspace{ OwnerID: member.ID, OrganizationID: owner.OrganizationID, AutomaticUpdates: database.AutomaticUpdatesAlways, - }).WithAgent().Do() + }).Seed(database.WorkspaceBuild{ + InitiatorID: member.ID, + }).Do() - workspace := build.Workspace - version1 := build.TemplateVersion + workspace := resp.Workspace + version1 := resp.TemplateVersion - version2 := dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{ - CreatedBy: owner.UserID, - OrganizationID: owner.OrganizationID, - TemplateID: uuid.NullUUID{UUID: version1.TemplateID.UUID, Valid: true}, - }).Params(param).Do() + version2 := dbfake.TemplateVersion(t, db). + Seed(database.TemplateVersion{ + CreatedBy: owner.UserID, + OrganizationID: owner.OrganizationID, + TemplateID: version1.TemplateID, + }). + Params(param).Do() // Autostart shouldn't be possible if parameters do not match. - resp, err := client.ResolveAutostart(ctx, workspace.ID.String()) + resolveResp, err := client.ResolveAutostart(ctx, workspace.ID.String()) require.NoError(t, err) - require.True(t, resp.ParameterMismatch) - - _ = dbfake.NewWorkspaceBuildBuilder(t, db, workspace).Seed(database.WorkspaceBuild{ - BuildNumber: 2, - TemplateVersionID: version2.ID, - }).Params(database.WorkspaceBuildParameter{ - Name: "param", - Value: "hello", - }).Do() + require.True(t, resolveResp.ParameterMismatch) + + _ = dbfake.WorkspaceBuild(t, db, workspace). + Seed(database.WorkspaceBuild{ + BuildNumber: 2, + TemplateVersionID: version2.TemplateVersion.ID, + }). + Params(database.WorkspaceBuildParameter{ + Name: "param", + Value: "hello", + }).Do() // We should be able to autostart since parameters are updated. - resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) + resolveResp, err = client.ResolveAutostart(ctx, workspace.ID.String()) require.NoError(t, err) - require.False(t, resp.ParameterMismatch) + require.False(t, resolveResp.ParameterMismatch) // Create another version that has the same parameters as version2. // We should be able to update without issue. _ = dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{ CreatedBy: owner.UserID, OrganizationID: owner.OrganizationID, - TemplateID: uuid.NullUUID{UUID: version1.TemplateID.UUID, Valid: true}, + TemplateID: version1.TemplateID, }).Params(param).Do() // Even though we're out of date we should still be able to autostart // since parameters resolve. - resp, err = client.ResolveAutostart(ctx, workspace.ID.String()) + resolveResp, err = client.ResolveAutostart(ctx, workspace.ID.String()) require.NoError(t, err) - require.False(t, resp.ParameterMismatch) + require.False(t, resolveResp.ParameterMismatch) } func TestAdminViewAllWorkspaces(t *testing.T) { diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 0834115b6348c..ef1a393f33fe2 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/google/uuid" "github.com/stretchr/testify/require" "cdr.dev/slog/sloggers/slogtest" @@ -1176,24 +1175,25 @@ func TestResolveAutostart(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - _, err := ownerClient.UpdateTemplateMeta(ctx, version1.TemplateID.UUID, codersdk.UpdateTemplateMeta{ + _, err := ownerClient.UpdateTemplateMeta(ctx, version1.Template.ID, codersdk.UpdateTemplateMeta{ RequireActiveVersion: true, }) require.NoError(t, err) client, member := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID) - workspace := dbfake.NewWorkspaceBuilder(t, db).Seed(database.Workspace{ - TemplateID: version1.TemplateID.UUID, - OwnerID: member.ID, - OrganizationID: owner.OrganizationID, - AutomaticUpdates: database.AutomaticUpdatesNever, - }).WithAgent().Do().Workspace + workspace := dbfake.WorkspaceBuild(t, db, database.Workspace{ + OwnerID: member.ID, + OrganizationID: owner.OrganizationID, + TemplateID: version1.Template.ID, + }).Seed(database.WorkspaceBuild{ + TemplateVersionID: version1.TemplateVersion.ID, + }).Do().Workspace _ = dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{ CreatedBy: owner.UserID, OrganizationID: owner.OrganizationID, - TemplateID: uuid.NullUUID{UUID: version1.TemplateID.UUID, Valid: true}, + TemplateID: version1.TemplateVersion.TemplateID, }).Params(database.TemplateVersionParameter{ Name: "param", Required: true, From 620d470d8cc4882beedb075ff6d48080aacb0cf0 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Wed, 29 Nov 2023 23:20:12 +0000 Subject: [PATCH 5/9] avoid exporting --- coderd/database/dbfake/dbfake.go | 29 +- coderd/database/dbgen/dbgen.go | 569 +++++++++++++++---------------- 2 files changed, 309 insertions(+), 289 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index eb020f77d0b45..63a678b6a3dd6 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -262,9 +262,9 @@ func (t TemplateVersionBuilder) Params(ps ...database.TemplateVersionParameter) func (t TemplateVersionBuilder) Do() TemplateVersionResponse { t.t.Helper() - t.seed.OrganizationID = dbgen.TakeFirst(t.seed.OrganizationID, uuid.New()) - t.seed.ID = dbgen.TakeFirst(t.seed.ID, uuid.New()) - t.seed.CreatedBy = dbgen.TakeFirst(t.seed.CreatedBy, uuid.New()) + t.seed.OrganizationID = takeFirst(t.seed.OrganizationID, uuid.New()) + t.seed.ID = takeFirst(t.seed.ID, uuid.New()) + t.seed.CreatedBy = takeFirst(t.seed.CreatedBy, uuid.New()) var resp TemplateVersionResponse if t.seed.TemplateID.UUID == uuid.Nil { @@ -403,3 +403,26 @@ func (b WorkspaceBuilder) Do() WorkspaceResponse { } return r } + +// takeFirstF takes the first value that returns true +func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { + for _, v := range values { + if take(v) { + return v + } + } + // If all empty, return the last element + if len(values) > 0 { + return values[len(values)-1] + } + var empty Value + return empty +} + +// takeFirst will take the first non-empty value. +func takeFirst[Value comparable](values ...Value) Value { + var empty Value + return takeFirstF(values, func(v Value) bool { + return v != empty + }) +} diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index af9ad0fa4272f..1848dc661531a 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -28,8 +28,8 @@ import ( // All methods take in a 'seed' object. Any provided fields in the seed will be // maintained. Any fields omitted will have sensible defaults generated. -// Ctx is to give all generator functions permission if the db is a dbauthz db. -var Ctx = dbauthz.As(context.Background(), rbac.Subject{ +// genCtx is to give all generator functions permission if the db is a dbauthz db. +var genCtx = dbauthz.As(context.Background(), rbac.Subject{ ID: "owner", Roles: rbac.Roles(must(rbac.RoleNames{rbac.RoleOwner()}.Expand())), Groups: []string{}, @@ -37,60 +37,60 @@ var Ctx = dbauthz.As(context.Background(), rbac.Subject{ }) func AuditLog(t testing.TB, db database.Store, seed database.AuditLog) database.AuditLog { - log, err := db.InsertAuditLog(Ctx, database.InsertAuditLogParams{ - ID: TakeFirst(seed.ID, uuid.New()), - Time: TakeFirst(seed.Time, dbtime.Now()), - UserID: TakeFirst(seed.UserID, uuid.New()), - OrganizationID: TakeFirst(seed.OrganizationID, uuid.New()), + log, err := db.InsertAuditLog(genCtx, database.InsertAuditLogParams{ + ID: takeFirst(seed.ID, uuid.New()), + Time: takeFirst(seed.Time, dbtime.Now()), + UserID: takeFirst(seed.UserID, uuid.New()), + OrganizationID: takeFirst(seed.OrganizationID, uuid.New()), Ip: pqtype.Inet{ IPNet: takeFirstIP(seed.Ip.IPNet, net.IPNet{}), - Valid: TakeFirst(seed.Ip.Valid, false), + Valid: takeFirst(seed.Ip.Valid, false), }, UserAgent: sql.NullString{ - String: TakeFirst(seed.UserAgent.String, ""), - Valid: TakeFirst(seed.UserAgent.Valid, false), + String: takeFirst(seed.UserAgent.String, ""), + Valid: takeFirst(seed.UserAgent.Valid, false), }, - ResourceType: TakeFirst(seed.ResourceType, database.ResourceTypeOrganization), - ResourceID: TakeFirst(seed.ResourceID, uuid.New()), - ResourceTarget: TakeFirst(seed.ResourceTarget, uuid.NewString()), - Action: TakeFirst(seed.Action, database.AuditActionCreate), - Diff: TakeFirstSlice(seed.Diff, []byte("{}")), - StatusCode: TakeFirst(seed.StatusCode, 200), - AdditionalFields: TakeFirstSlice(seed.Diff, []byte("{}")), - RequestID: TakeFirst(seed.RequestID, uuid.New()), - ResourceIcon: TakeFirst(seed.ResourceIcon, ""), + ResourceType: takeFirst(seed.ResourceType, database.ResourceTypeOrganization), + ResourceID: takeFirst(seed.ResourceID, uuid.New()), + ResourceTarget: takeFirst(seed.ResourceTarget, uuid.NewString()), + Action: takeFirst(seed.Action, database.AuditActionCreate), + Diff: takeFirstSlice(seed.Diff, []byte("{}")), + StatusCode: takeFirst(seed.StatusCode, 200), + AdditionalFields: takeFirstSlice(seed.Diff, []byte("{}")), + RequestID: takeFirst(seed.RequestID, uuid.New()), + ResourceIcon: takeFirst(seed.ResourceIcon, ""), }) require.NoError(t, err, "insert audit log") return log } func Template(t testing.TB, db database.Store, seed database.Template) database.Template { - id := TakeFirst(seed.ID, uuid.New()) + id := takeFirst(seed.ID, uuid.New()) if seed.GroupACL == nil { // By default, all users in the organization can read the template. seed.GroupACL = database.TemplateACL{ seed.OrganizationID.String(): []rbac.Action{rbac.ActionRead}, } } - err := db.InsertTemplate(Ctx, database.InsertTemplateParams{ + err := db.InsertTemplate(genCtx, database.InsertTemplateParams{ ID: id, - CreatedAt: TakeFirst(seed.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(seed.UpdatedAt, dbtime.Now()), - OrganizationID: TakeFirst(seed.OrganizationID, uuid.New()), - Name: TakeFirst(seed.Name, namesgenerator.GetRandomName(1)), - Provisioner: TakeFirst(seed.Provisioner, database.ProvisionerTypeEcho), - ActiveVersionID: TakeFirst(seed.ActiveVersionID, uuid.New()), - Description: TakeFirst(seed.Description, namesgenerator.GetRandomName(1)), - CreatedBy: TakeFirst(seed.CreatedBy, uuid.New()), - Icon: TakeFirst(seed.Icon, namesgenerator.GetRandomName(1)), + CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(seed.UpdatedAt, dbtime.Now()), + OrganizationID: takeFirst(seed.OrganizationID, uuid.New()), + Name: takeFirst(seed.Name, namesgenerator.GetRandomName(1)), + Provisioner: takeFirst(seed.Provisioner, database.ProvisionerTypeEcho), + ActiveVersionID: takeFirst(seed.ActiveVersionID, uuid.New()), + Description: takeFirst(seed.Description, namesgenerator.GetRandomName(1)), + CreatedBy: takeFirst(seed.CreatedBy, uuid.New()), + Icon: takeFirst(seed.Icon, namesgenerator.GetRandomName(1)), UserACL: seed.UserACL, GroupACL: seed.GroupACL, - DisplayName: TakeFirst(seed.DisplayName, namesgenerator.GetRandomName(1)), + DisplayName: takeFirst(seed.DisplayName, namesgenerator.GetRandomName(1)), AllowUserCancelWorkspaceJobs: seed.AllowUserCancelWorkspaceJobs, }) require.NoError(t, err, "insert template") - template, err := db.GetTemplateByID(Ctx, id) + template, err := db.GetTemplateByID(genCtx, id) require.NoError(t, err, "get template") return template } @@ -111,55 +111,55 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey) (key database } } - key, err := db.InsertAPIKey(Ctx, database.InsertAPIKeyParams{ - ID: TakeFirst(seed.ID, id), + key, err := db.InsertAPIKey(genCtx, database.InsertAPIKeyParams{ + ID: takeFirst(seed.ID, id), // 0 defaults to 86400 at the db layer - LifetimeSeconds: TakeFirst(seed.LifetimeSeconds, 0), - HashedSecret: TakeFirstSlice(seed.HashedSecret, hashed[:]), + LifetimeSeconds: takeFirst(seed.LifetimeSeconds, 0), + HashedSecret: takeFirstSlice(seed.HashedSecret, hashed[:]), IPAddress: ip, - UserID: TakeFirst(seed.UserID, uuid.New()), - LastUsed: TakeFirst(seed.LastUsed, dbtime.Now()), - ExpiresAt: TakeFirst(seed.ExpiresAt, dbtime.Now().Add(time.Hour)), - CreatedAt: TakeFirst(seed.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(seed.UpdatedAt, dbtime.Now()), - LoginType: TakeFirst(seed.LoginType, database.LoginTypePassword), - Scope: TakeFirst(seed.Scope, database.APIKeyScopeAll), - TokenName: TakeFirst(seed.TokenName), + UserID: takeFirst(seed.UserID, uuid.New()), + LastUsed: takeFirst(seed.LastUsed, dbtime.Now()), + ExpiresAt: takeFirst(seed.ExpiresAt, dbtime.Now().Add(time.Hour)), + CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(seed.UpdatedAt, dbtime.Now()), + LoginType: takeFirst(seed.LoginType, database.LoginTypePassword), + Scope: takeFirst(seed.Scope, database.APIKeyScopeAll), + TokenName: takeFirst(seed.TokenName), }) require.NoError(t, err, "insert api key") return key, fmt.Sprintf("%s-%s", key.ID, secret) } func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent { - agt, err := db.InsertWorkspaceAgent(Ctx, database.InsertWorkspaceAgentParams{ - ID: TakeFirst(orig.ID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), - ResourceID: TakeFirst(orig.ResourceID, uuid.New()), - AuthToken: TakeFirst(orig.AuthToken, uuid.New()), + agt, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{ + ID: takeFirst(orig.ID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + ResourceID: takeFirst(orig.ResourceID, uuid.New()), + AuthToken: takeFirst(orig.AuthToken, uuid.New()), AuthInstanceID: sql.NullString{ - String: TakeFirst(orig.AuthInstanceID.String, namesgenerator.GetRandomName(1)), - Valid: TakeFirst(orig.AuthInstanceID.Valid, true), + String: takeFirst(orig.AuthInstanceID.String, namesgenerator.GetRandomName(1)), + Valid: takeFirst(orig.AuthInstanceID.Valid, true), }, - Architecture: TakeFirst(orig.Architecture, "amd64"), + Architecture: takeFirst(orig.Architecture, "amd64"), EnvironmentVariables: pqtype.NullRawMessage{ - RawMessage: TakeFirstSlice(orig.EnvironmentVariables.RawMessage, []byte("{}")), - Valid: TakeFirst(orig.EnvironmentVariables.Valid, false), + RawMessage: takeFirstSlice(orig.EnvironmentVariables.RawMessage, []byte("{}")), + Valid: takeFirst(orig.EnvironmentVariables.Valid, false), }, - OperatingSystem: TakeFirst(orig.OperatingSystem, "linux"), - Directory: TakeFirst(orig.Directory, ""), + OperatingSystem: takeFirst(orig.OperatingSystem, "linux"), + Directory: takeFirst(orig.Directory, ""), InstanceMetadata: pqtype.NullRawMessage{ - RawMessage: TakeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), - Valid: TakeFirst(orig.ResourceMetadata.Valid, false), + RawMessage: takeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), + Valid: takeFirst(orig.ResourceMetadata.Valid, false), }, ResourceMetadata: pqtype.NullRawMessage{ - RawMessage: TakeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), - Valid: TakeFirst(orig.ResourceMetadata.Valid, false), + RawMessage: takeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), + Valid: takeFirst(orig.ResourceMetadata.Valid, false), }, - ConnectionTimeoutSeconds: TakeFirst(orig.ConnectionTimeoutSeconds, 3600), - TroubleshootingURL: TakeFirst(orig.TroubleshootingURL, "https://example.com"), - MOTDFile: TakeFirst(orig.TroubleshootingURL, ""), + ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600), + TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"), + MOTDFile: takeFirst(orig.TroubleshootingURL, ""), DisplayApps: append([]database.DisplayApp{}, orig.DisplayApps...), }) require.NoError(t, err, "insert workspace agent") @@ -169,30 +169,30 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace { t.Helper() - workspace, err := db.InsertWorkspace(Ctx, database.InsertWorkspaceParams{ - ID: TakeFirst(orig.ID, uuid.New()), - OwnerID: TakeFirst(orig.OwnerID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), - TemplateID: TakeFirst(orig.TemplateID, uuid.New()), - LastUsedAt: TakeFirst(orig.LastUsedAt, dbtime.Now()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + workspace, err := db.InsertWorkspace(genCtx, database.InsertWorkspaceParams{ + ID: takeFirst(orig.ID, uuid.New()), + OwnerID: takeFirst(orig.OwnerID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), + TemplateID: takeFirst(orig.TemplateID, uuid.New()), + LastUsedAt: takeFirst(orig.LastUsedAt, dbtime.Now()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), AutostartSchedule: orig.AutostartSchedule, Ttl: orig.Ttl, - AutomaticUpdates: TakeFirst(orig.AutomaticUpdates, database.AutomaticUpdatesNever), + AutomaticUpdates: takeFirst(orig.AutomaticUpdates, database.AutomaticUpdatesNever), }) require.NoError(t, err, "insert workspace") return workspace } func WorkspaceAgentLogSource(t testing.TB, db database.Store, orig database.WorkspaceAgentLogSource) database.WorkspaceAgentLogSource { - sources, err := db.InsertWorkspaceAgentLogSources(Ctx, database.InsertWorkspaceAgentLogSourcesParams{ - WorkspaceAgentID: TakeFirst(orig.WorkspaceAgentID, uuid.New()), - ID: []uuid.UUID{TakeFirst(orig.ID, uuid.New())}, - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - DisplayName: []string{TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1))}, - Icon: []string{TakeFirst(orig.Icon, namesgenerator.GetRandomName(1))}, + sources, err := db.InsertWorkspaceAgentLogSources(genCtx, database.InsertWorkspaceAgentLogSourcesParams{ + WorkspaceAgentID: takeFirst(orig.WorkspaceAgentID, uuid.New()), + ID: []uuid.UUID{takeFirst(orig.ID, uuid.New())}, + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + DisplayName: []string{takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1))}, + Icon: []string{takeFirst(orig.Icon, namesgenerator.GetRandomName(1))}, }) require.NoError(t, err, "insert workspace agent log source") return sources[0] @@ -201,28 +201,28 @@ func WorkspaceAgentLogSource(t testing.TB, db database.Store, orig database.Work func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuild { t.Helper() - buildID := TakeFirst(orig.ID, uuid.New()) + buildID := takeFirst(orig.ID, uuid.New()) var build database.WorkspaceBuild err := db.InTx(func(db database.Store) error { - err := db.InsertWorkspaceBuild(Ctx, database.InsertWorkspaceBuildParams{ + err := db.InsertWorkspaceBuild(genCtx, database.InsertWorkspaceBuildParams{ ID: buildID, - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.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, dbtime.Now().Add(time.Hour)), - MaxDeadline: TakeFirst(orig.MaxDeadline, time.Time{}), - Reason: TakeFirst(orig.Reason, database.BuildReasonInitiator), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.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, dbtime.Now().Add(time.Hour)), + MaxDeadline: takeFirst(orig.MaxDeadline, time.Time{}), + Reason: takeFirst(orig.Reason, database.BuildReasonInitiator), }) if err != nil { return err } - build, err = db.GetWorkspaceBuildByID(Ctx, buildID) + build, err = db.GetWorkspaceBuildByID(genCtx, buildID) if err != nil { return err } @@ -248,8 +248,8 @@ func WorkspaceBuildParameters(t testing.TB, db database.Store, orig []database.W values = append(values, param.Value) } err := db.InTx(func(tx database.Store) error { - id := TakeFirst(orig[0].WorkspaceBuildID, uuid.New()) - err := tx.InsertWorkspaceBuildParameters(Ctx, database.InsertWorkspaceBuildParametersParams{ + id := takeFirst(orig[0].WorkspaceBuildID, uuid.New()) + err := tx.InsertWorkspaceBuildParameters(genCtx, database.InsertWorkspaceBuildParametersParams{ WorkspaceBuildID: id, Name: names, Value: values, @@ -258,10 +258,7 @@ func WorkspaceBuildParameters(t testing.TB, db database.Store, orig []database.W return err } - params, err = tx.GetWorkspaceBuildParameters(Ctx, id) - if err != nil { - return err - } + params, err = tx.GetWorkspaceBuildParameters(genCtx, id) return err }, nil) require.NoError(t, err) @@ -269,27 +266,27 @@ func WorkspaceBuildParameters(t testing.TB, db database.Store, orig []database.W } func User(t testing.TB, db database.Store, orig database.User) database.User { - user, err := db.InsertUser(Ctx, database.InsertUserParams{ - ID: TakeFirst(orig.ID, uuid.New()), - Email: TakeFirst(orig.Email, namesgenerator.GetRandomName(1)), - Username: TakeFirst(orig.Username, namesgenerator.GetRandomName(1)), - HashedPassword: TakeFirstSlice(orig.HashedPassword, []byte(must(cryptorand.String(32)))), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - RBACRoles: TakeFirstSlice(orig.RBACRoles, []string{}), - LoginType: TakeFirst(orig.LoginType, database.LoginTypePassword), + user, err := db.InsertUser(genCtx, database.InsertUserParams{ + ID: takeFirst(orig.ID, uuid.New()), + Email: takeFirst(orig.Email, namesgenerator.GetRandomName(1)), + Username: takeFirst(orig.Username, namesgenerator.GetRandomName(1)), + HashedPassword: takeFirstSlice(orig.HashedPassword, []byte(must(cryptorand.String(32)))), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + RBACRoles: takeFirstSlice(orig.RBACRoles, []string{}), + LoginType: takeFirst(orig.LoginType, database.LoginTypePassword), }) require.NoError(t, err, "insert user") - user, err = db.UpdateUserStatus(Ctx, database.UpdateUserStatusParams{ + user, err = db.UpdateUserStatus(genCtx, database.UpdateUserStatusParams{ ID: user.ID, - Status: TakeFirst(orig.Status, database.UserStatusActive), + Status: takeFirst(orig.Status, database.UserStatusActive), UpdatedAt: dbtime.Now(), }) require.NoError(t, err, "insert user") if !orig.LastSeenAt.IsZero() { - user, err = db.UpdateUserLastSeenAt(Ctx, database.UpdateUserLastSeenAtParams{ + user, err = db.UpdateUserLastSeenAt(genCtx, database.UpdateUserLastSeenAtParams{ ID: user.ID, LastSeenAt: orig.LastSeenAt, UpdatedAt: user.UpdatedAt, @@ -298,7 +295,7 @@ func User(t testing.TB, db database.Store, orig database.User) database.User { } if orig.Deleted { - err = db.UpdateUserDeletedByID(Ctx, database.UpdateUserDeletedByIDParams{ + err = db.UpdateUserDeletedByID(genCtx, database.UpdateUserDeletedByIDParams{ ID: user.ID, Deleted: orig.Deleted, }) @@ -308,50 +305,50 @@ func User(t testing.TB, db database.Store, orig database.User) database.User { } func GitSSHKey(t testing.TB, db database.Store, orig database.GitSSHKey) database.GitSSHKey { - key, err := db.InsertGitSSHKey(Ctx, database.InsertGitSSHKeyParams{ - UserID: TakeFirst(orig.UserID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - PrivateKey: TakeFirst(orig.PrivateKey, ""), - PublicKey: TakeFirst(orig.PublicKey, ""), + key, err := db.InsertGitSSHKey(genCtx, database.InsertGitSSHKeyParams{ + UserID: takeFirst(orig.UserID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + PrivateKey: takeFirst(orig.PrivateKey, ""), + PublicKey: takeFirst(orig.PublicKey, ""), }) require.NoError(t, err, "insert ssh key") return key } func Organization(t testing.TB, db database.Store, orig database.Organization) database.Organization { - org, err := db.InsertOrganization(Ctx, database.InsertOrganizationParams{ - ID: TakeFirst(orig.ID, uuid.New()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Description: TakeFirst(orig.Description, namesgenerator.GetRandomName(1)), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + org, err := db.InsertOrganization(genCtx, database.InsertOrganizationParams{ + ID: takeFirst(orig.ID, uuid.New()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Description: takeFirst(orig.Description, namesgenerator.GetRandomName(1)), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), }) require.NoError(t, err, "insert organization") return org } func OrganizationMember(t testing.TB, db database.Store, orig database.OrganizationMember) database.OrganizationMember { - mem, err := db.InsertOrganizationMember(Ctx, database.InsertOrganizationMemberParams{ - OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), - UserID: TakeFirst(orig.UserID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - Roles: TakeFirstSlice(orig.Roles, []string{}), + mem, err := db.InsertOrganizationMember(genCtx, database.InsertOrganizationMemberParams{ + OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), + UserID: takeFirst(orig.UserID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + Roles: takeFirstSlice(orig.Roles, []string{}), }) require.NoError(t, err, "insert organization") return mem } func Group(t testing.TB, db database.Store, orig database.Group) database.Group { - name := TakeFirst(orig.Name, namesgenerator.GetRandomName(1)) - group, err := db.InsertGroup(Ctx, database.InsertGroupParams{ - ID: TakeFirst(orig.ID, uuid.New()), + name := takeFirst(orig.Name, namesgenerator.GetRandomName(1)) + group, err := db.InsertGroup(genCtx, database.InsertGroupParams{ + ID: takeFirst(orig.ID, uuid.New()), Name: name, - DisplayName: TakeFirst(orig.DisplayName, name), - OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), - AvatarURL: TakeFirst(orig.AvatarURL, "https://logo.example.com"), - QuotaAllowance: TakeFirst(orig.QuotaAllowance, 0), + DisplayName: takeFirst(orig.DisplayName, name), + OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), + AvatarURL: takeFirst(orig.AvatarURL, "https://logo.example.com"), + QuotaAllowance: takeFirst(orig.QuotaAllowance, 0), }) require.NoError(t, err, "insert group") return group @@ -359,11 +356,11 @@ func Group(t testing.TB, db database.Store, orig database.Group) database.Group func GroupMember(t testing.TB, db database.Store, orig database.GroupMember) database.GroupMember { member := database.GroupMember{ - UserID: TakeFirst(orig.UserID, uuid.New()), - GroupID: TakeFirst(orig.GroupID, uuid.New()), + UserID: takeFirst(orig.UserID, uuid.New()), + GroupID: takeFirst(orig.GroupID, uuid.New()), } //nolint:gosimple - err := db.InsertGroupMember(Ctx, database.InsertGroupMemberParams{ + err := db.InsertGroupMember(genCtx, database.InsertGroupMemberParams{ UserID: member.UserID, GroupID: member.GroupID, }) @@ -376,7 +373,7 @@ func GroupMember(t testing.TB, db database.Store, orig database.GroupMember) dat func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig database.ProvisionerJob) database.ProvisionerJob { t.Helper() - jobID := TakeFirst(orig.ID, uuid.New()) + jobID := takeFirst(orig.ID, uuid.New()) // Always set some tags to prevent Acquire from grabbing jobs it should not. if !orig.StartedAt.Time.IsZero() { if orig.Tags == nil { @@ -386,17 +383,17 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data orig.Tags[jobID.String()] = "true" } - job, err := db.InsertProvisionerJob(Ctx, database.InsertProvisionerJobParams{ + job, err := db.InsertProvisionerJob(genCtx, database.InsertProvisionerJobParams{ ID: jobID, - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), - InitiatorID: TakeFirst(orig.InitiatorID, uuid.New()), - Provisioner: TakeFirst(orig.Provisioner, database.ProvisionerTypeEcho), - StorageMethod: TakeFirst(orig.StorageMethod, database.ProvisionerStorageMethodFile), - FileID: TakeFirst(orig.FileID, uuid.New()), - Type: TakeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild), - Input: TakeFirstSlice(orig.Input, []byte("{}")), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), + InitiatorID: takeFirst(orig.InitiatorID, uuid.New()), + Provisioner: takeFirst(orig.Provisioner, database.ProvisionerTypeEcho), + StorageMethod: takeFirst(orig.StorageMethod, database.ProvisionerStorageMethodFile), + FileID: takeFirst(orig.FileID, uuid.New()), + Type: takeFirst(orig.Type, database.ProvisionerJobTypeWorkspaceBuild), + Input: takeFirstSlice(orig.Input, []byte("{}")), Tags: orig.Tags, TraceMetadata: pqtype.NullRawMessage{}, }) @@ -406,7 +403,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data require.NoError(t, err, "post job to pubsub") } if !orig.StartedAt.Time.IsZero() { - job, err = db.AcquireProvisionerJob(Ctx, database.AcquireProvisionerJobParams{ + job, err = db.AcquireProvisionerJob(genCtx, database.AcquireProvisionerJobParams{ StartedAt: orig.StartedAt, Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, Tags: must(json.Marshal(orig.Tags)), @@ -418,7 +415,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data } if !orig.CompletedAt.Time.IsZero() || orig.Error.String != "" { - err := db.UpdateProvisionerJobWithCompleteByID(Ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ + err := db.UpdateProvisionerJobWithCompleteByID(genCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: jobID, UpdatedAt: job.UpdatedAt, CompletedAt: orig.CompletedAt, @@ -428,7 +425,7 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data require.NoError(t, err) } if !orig.CanceledAt.Time.IsZero() { - err := db.UpdateProvisionerJobWithCancelByID(Ctx, database.UpdateProvisionerJobWithCancelByIDParams{ + err := db.UpdateProvisionerJobWithCancelByID(genCtx, database.UpdateProvisionerJobWithCancelByIDParams{ ID: jobID, CanceledAt: orig.CanceledAt, CompletedAt: orig.CompletedAt, @@ -436,66 +433,66 @@ func ProvisionerJob(t testing.TB, db database.Store, ps pubsub.Pubsub, orig data require.NoError(t, err) } - job, err = db.GetProvisionerJobByID(Ctx, jobID) + job, err = db.GetProvisionerJobByID(genCtx, jobID) require.NoError(t, err) return job } func WorkspaceApp(t testing.TB, db database.Store, orig database.WorkspaceApp) database.WorkspaceApp { - resource, err := db.InsertWorkspaceApp(Ctx, database.InsertWorkspaceAppParams{ - ID: TakeFirst(orig.ID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - AgentID: TakeFirst(orig.AgentID, uuid.New()), - Slug: TakeFirst(orig.Slug, namesgenerator.GetRandomName(1)), - DisplayName: TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), - Icon: TakeFirst(orig.Icon, namesgenerator.GetRandomName(1)), + resource, err := db.InsertWorkspaceApp(genCtx, database.InsertWorkspaceAppParams{ + ID: takeFirst(orig.ID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + AgentID: takeFirst(orig.AgentID, uuid.New()), + Slug: takeFirst(orig.Slug, namesgenerator.GetRandomName(1)), + DisplayName: takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), + Icon: takeFirst(orig.Icon, namesgenerator.GetRandomName(1)), Command: sql.NullString{ - String: TakeFirst(orig.Command.String, "ls"), + String: takeFirst(orig.Command.String, "ls"), Valid: orig.Command.Valid, }, Url: sql.NullString{ - String: TakeFirst(orig.Url.String), + String: takeFirst(orig.Url.String), Valid: orig.Url.Valid, }, External: orig.External, Subdomain: orig.Subdomain, - SharingLevel: TakeFirst(orig.SharingLevel, database.AppSharingLevelOwner), - HealthcheckUrl: TakeFirst(orig.HealthcheckUrl, "https://localhost:8000"), - HealthcheckInterval: TakeFirst(orig.HealthcheckInterval, 60), - HealthcheckThreshold: TakeFirst(orig.HealthcheckThreshold, 60), - Health: TakeFirst(orig.Health, database.WorkspaceAppHealthHealthy), + SharingLevel: takeFirst(orig.SharingLevel, database.AppSharingLevelOwner), + HealthcheckUrl: takeFirst(orig.HealthcheckUrl, "https://localhost:8000"), + HealthcheckInterval: takeFirst(orig.HealthcheckInterval, 60), + HealthcheckThreshold: takeFirst(orig.HealthcheckThreshold, 60), + Health: takeFirst(orig.Health, database.WorkspaceAppHealthHealthy), }) require.NoError(t, err, "insert app") return resource } func WorkspaceResource(t testing.TB, db database.Store, orig database.WorkspaceResource) database.WorkspaceResource { - resource, err := db.InsertWorkspaceResource(Ctx, database.InsertWorkspaceResourceParams{ - ID: TakeFirst(orig.ID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - JobID: TakeFirst(orig.JobID, uuid.New()), - Transition: TakeFirst(orig.Transition, database.WorkspaceTransitionStart), - Type: TakeFirst(orig.Type, "fake_resource"), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Hide: TakeFirst(orig.Hide, false), - Icon: TakeFirst(orig.Icon, ""), + resource, err := db.InsertWorkspaceResource(genCtx, database.InsertWorkspaceResourceParams{ + ID: takeFirst(orig.ID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + JobID: takeFirst(orig.JobID, uuid.New()), + Transition: takeFirst(orig.Transition, database.WorkspaceTransitionStart), + Type: takeFirst(orig.Type, "fake_resource"), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Hide: takeFirst(orig.Hide, false), + Icon: takeFirst(orig.Icon, ""), InstanceType: sql.NullString{ - String: TakeFirst(orig.InstanceType.String, ""), - Valid: TakeFirst(orig.InstanceType.Valid, false), + String: takeFirst(orig.InstanceType.String, ""), + Valid: takeFirst(orig.InstanceType.Valid, false), }, - DailyCost: TakeFirst(orig.DailyCost, 0), + DailyCost: takeFirst(orig.DailyCost, 0), }) require.NoError(t, err, "insert resource") return resource } func WorkspaceResourceMetadatums(t testing.TB, db database.Store, seed database.WorkspaceResourceMetadatum) []database.WorkspaceResourceMetadatum { - meta, err := db.InsertWorkspaceResourceMetadata(Ctx, database.InsertWorkspaceResourceMetadataParams{ - WorkspaceResourceID: TakeFirst(seed.WorkspaceResourceID, uuid.New()), - Key: []string{TakeFirst(seed.Key, namesgenerator.GetRandomName(1))}, - Value: []string{TakeFirst(seed.Value.String, namesgenerator.GetRandomName(1))}, - Sensitive: []bool{TakeFirst(seed.Sensitive, false)}, + meta, err := db.InsertWorkspaceResourceMetadata(genCtx, database.InsertWorkspaceResourceMetadataParams{ + WorkspaceResourceID: takeFirst(seed.WorkspaceResourceID, uuid.New()), + Key: []string{takeFirst(seed.Key, namesgenerator.GetRandomName(1))}, + Value: []string{takeFirst(seed.Value.String, namesgenerator.GetRandomName(1))}, + Sensitive: []bool{takeFirst(seed.Sensitive, false)}, }) require.NoError(t, err, "insert meta data") return meta @@ -506,22 +503,22 @@ func WorkspaceProxy(t testing.TB, db database.Store, orig database.WorkspaceProx require.NoError(t, err, "generate secret") hashedSecret := sha256.Sum256([]byte(secret)) - proxy, err := db.InsertWorkspaceProxy(Ctx, database.InsertWorkspaceProxyParams{ - ID: TakeFirst(orig.ID, uuid.New()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), - DisplayName: TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), - Icon: TakeFirst(orig.Icon, namesgenerator.GetRandomName(1)), + proxy, err := db.InsertWorkspaceProxy(genCtx, database.InsertWorkspaceProxyParams{ + ID: takeFirst(orig.ID, uuid.New()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + DisplayName: takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), + Icon: takeFirst(orig.Icon, namesgenerator.GetRandomName(1)), TokenHashedSecret: hashedSecret[:], - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - DerpEnabled: TakeFirst(orig.DerpEnabled, false), - DerpOnly: TakeFirst(orig.DerpEnabled, false), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + DerpEnabled: takeFirst(orig.DerpEnabled, false), + DerpOnly: takeFirst(orig.DerpEnabled, false), }) require.NoError(t, err, "insert proxy") // Also set these fields if the caller wants them. if orig.Url != "" || orig.WildcardHostname != "" { - proxy, err = db.RegisterWorkspaceProxy(Ctx, database.RegisterWorkspaceProxyParams{ + proxy, err = db.RegisterWorkspaceProxy(genCtx, database.RegisterWorkspaceProxyParams{ Url: orig.Url, WildcardHostname: orig.WildcardHostname, ID: proxy.ID, @@ -532,29 +529,29 @@ func WorkspaceProxy(t testing.TB, db database.Store, orig database.WorkspaceProx } func File(t testing.TB, db database.Store, orig database.File) database.File { - file, err := db.InsertFile(Ctx, database.InsertFileParams{ - ID: TakeFirst(orig.ID, uuid.New()), - Hash: TakeFirst(orig.Hash, hex.EncodeToString(make([]byte, 32))), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - CreatedBy: TakeFirst(orig.CreatedBy, uuid.New()), - Mimetype: TakeFirst(orig.Mimetype, "application/x-tar"), - Data: TakeFirstSlice(orig.Data, []byte{}), + file, err := db.InsertFile(genCtx, database.InsertFileParams{ + ID: takeFirst(orig.ID, uuid.New()), + Hash: takeFirst(orig.Hash, hex.EncodeToString(make([]byte, 32))), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + CreatedBy: takeFirst(orig.CreatedBy, uuid.New()), + Mimetype: takeFirst(orig.Mimetype, "application/x-tar"), + Data: takeFirstSlice(orig.Data, []byte{}), }) require.NoError(t, err, "insert file") return file } func UserLink(t testing.TB, db database.Store, orig database.UserLink) database.UserLink { - link, err := db.InsertUserLink(Ctx, database.InsertUserLinkParams{ - UserID: TakeFirst(orig.UserID, uuid.New()), - LoginType: TakeFirst(orig.LoginType, database.LoginTypeGithub), - LinkedID: TakeFirst(orig.LinkedID), - OAuthAccessToken: TakeFirst(orig.OAuthAccessToken, uuid.NewString()), - OAuthAccessTokenKeyID: TakeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), - OAuthRefreshToken: TakeFirst(orig.OAuthRefreshToken, uuid.NewString()), - OAuthRefreshTokenKeyID: TakeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), - OAuthExpiry: TakeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), - DebugContext: TakeFirstSlice(orig.DebugContext, json.RawMessage("{}")), + link, err := db.InsertUserLink(genCtx, database.InsertUserLinkParams{ + UserID: takeFirst(orig.UserID, uuid.New()), + LoginType: takeFirst(orig.LoginType, database.LoginTypeGithub), + LinkedID: takeFirst(orig.LinkedID), + OAuthAccessToken: takeFirst(orig.OAuthAccessToken, uuid.NewString()), + OAuthAccessTokenKeyID: takeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), + OAuthRefreshToken: takeFirst(orig.OAuthRefreshToken, uuid.NewString()), + OAuthRefreshTokenKeyID: takeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), + OAuthExpiry: takeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), + DebugContext: takeFirstSlice(orig.DebugContext, json.RawMessage("{}")), }) require.NoError(t, err, "insert link") @@ -562,17 +559,17 @@ func UserLink(t testing.TB, db database.Store, orig database.UserLink) database. } func ExternalAuthLink(t testing.TB, db database.Store, orig database.ExternalAuthLink) database.ExternalAuthLink { - msg := TakeFirst(&orig.OAuthExtra, &pqtype.NullRawMessage{}) - link, err := db.InsertExternalAuthLink(Ctx, database.InsertExternalAuthLinkParams{ - ProviderID: TakeFirst(orig.ProviderID, uuid.New().String()), - UserID: TakeFirst(orig.UserID, uuid.New()), - OAuthAccessToken: TakeFirst(orig.OAuthAccessToken, uuid.NewString()), - OAuthAccessTokenKeyID: TakeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), - OAuthRefreshToken: TakeFirst(orig.OAuthRefreshToken, uuid.NewString()), - OAuthRefreshTokenKeyID: TakeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), - OAuthExpiry: TakeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), + msg := takeFirst(&orig.OAuthExtra, &pqtype.NullRawMessage{}) + link, err := db.InsertExternalAuthLink(genCtx, database.InsertExternalAuthLinkParams{ + ProviderID: takeFirst(orig.ProviderID, uuid.New().String()), + UserID: takeFirst(orig.UserID, uuid.New()), + OAuthAccessToken: takeFirst(orig.OAuthAccessToken, uuid.NewString()), + OAuthAccessTokenKeyID: takeFirst(orig.OAuthAccessTokenKeyID, sql.NullString{}), + OAuthRefreshToken: takeFirst(orig.OAuthRefreshToken, uuid.NewString()), + OAuthRefreshTokenKeyID: takeFirst(orig.OAuthRefreshTokenKeyID, sql.NullString{}), + OAuthExpiry: takeFirst(orig.OAuthExpiry, dbtime.Now().Add(time.Hour*24)), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), OAuthExtra: *msg, }) @@ -583,24 +580,24 @@ func ExternalAuthLink(t testing.TB, db database.Store, orig database.ExternalAut func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVersion) database.TemplateVersion { var version database.TemplateVersion err := db.InTx(func(db database.Store) error { - versionID := TakeFirst(orig.ID, uuid.New()) - err := db.InsertTemplateVersion(Ctx, database.InsertTemplateVersionParams{ + versionID := takeFirst(orig.ID, uuid.New()) + err := db.InsertTemplateVersion(genCtx, database.InsertTemplateVersionParams{ ID: versionID, - TemplateID: TakeFirst(orig.TemplateID, uuid.NullUUID{}), - OrganizationID: TakeFirst(orig.OrganizationID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: TakeFirst(orig.UpdatedAt, dbtime.Now()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), + TemplateID: takeFirst(orig.TemplateID, uuid.NullUUID{}), + OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), Message: orig.Message, - Readme: TakeFirst(orig.Readme, namesgenerator.GetRandomName(1)), - JobID: TakeFirst(orig.JobID, uuid.New()), - CreatedBy: TakeFirst(orig.CreatedBy, uuid.New()), + Readme: takeFirst(orig.Readme, namesgenerator.GetRandomName(1)), + JobID: takeFirst(orig.JobID, uuid.New()), + CreatedBy: takeFirst(orig.CreatedBy, uuid.New()), }) if err != nil { return err } - version, err = db.GetTemplateVersionByID(Ctx, versionID) + version, err = db.GetTemplateVersionByID(genCtx, versionID) if err != nil { return err } @@ -612,15 +609,15 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers } func TemplateVersionVariable(t testing.TB, db database.Store, orig database.TemplateVersionVariable) database.TemplateVersionVariable { - version, err := db.InsertTemplateVersionVariable(Ctx, database.InsertTemplateVersionVariableParams{ - TemplateVersionID: TakeFirst(orig.TemplateVersionID, uuid.New()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Description: TakeFirst(orig.Description, namesgenerator.GetRandomName(1)), - Type: TakeFirst(orig.Type, "string"), - Value: TakeFirst(orig.Value, ""), - DefaultValue: TakeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), - Required: TakeFirst(orig.Required, false), - Sensitive: TakeFirst(orig.Sensitive, false), + version, err := db.InsertTemplateVersionVariable(genCtx, database.InsertTemplateVersionVariableParams{ + TemplateVersionID: takeFirst(orig.TemplateVersionID, uuid.New()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Description: takeFirst(orig.Description, namesgenerator.GetRandomName(1)), + Type: takeFirst(orig.Type, "string"), + Value: takeFirst(orig.Value, ""), + DefaultValue: takeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), + Required: takeFirst(orig.Required, false), + Sensitive: takeFirst(orig.Sensitive, false), }) require.NoError(t, err, "insert template version variable") return version @@ -629,24 +626,24 @@ func TemplateVersionVariable(t testing.TB, db database.Store, orig database.Temp func TemplateVersionParameter(t testing.TB, db database.Store, orig database.TemplateVersionParameter) database.TemplateVersionParameter { t.Helper() - version, err := db.InsertTemplateVersionParameter(Ctx, database.InsertTemplateVersionParameterParams{ - TemplateVersionID: TakeFirst(orig.TemplateVersionID, uuid.New()), - Name: TakeFirst(orig.Name, namesgenerator.GetRandomName(1)), - Description: TakeFirst(orig.Description, namesgenerator.GetRandomName(1)), - Type: TakeFirst(orig.Type, "string"), - Mutable: TakeFirst(orig.Mutable, false), - DefaultValue: TakeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), - Icon: TakeFirst(orig.Icon, namesgenerator.GetRandomName(1)), - Options: TakeFirstSlice(orig.Options, []byte("[]")), - ValidationRegex: TakeFirst(orig.ValidationRegex, ""), - ValidationMin: TakeFirst(orig.ValidationMin, sql.NullInt32{}), - ValidationMax: TakeFirst(orig.ValidationMax, sql.NullInt32{}), - ValidationError: TakeFirst(orig.ValidationError, ""), - ValidationMonotonic: TakeFirst(orig.ValidationMonotonic, ""), - Required: TakeFirst(orig.Required, false), - DisplayName: TakeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), - DisplayOrder: TakeFirst(orig.DisplayOrder, 0), - Ephemeral: TakeFirst(orig.Ephemeral, false), + version, err := db.InsertTemplateVersionParameter(genCtx, database.InsertTemplateVersionParameterParams{ + TemplateVersionID: takeFirst(orig.TemplateVersionID, uuid.New()), + Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)), + Description: takeFirst(orig.Description, namesgenerator.GetRandomName(1)), + Type: takeFirst(orig.Type, "string"), + Mutable: takeFirst(orig.Mutable, false), + DefaultValue: takeFirst(orig.DefaultValue, namesgenerator.GetRandomName(1)), + Icon: takeFirst(orig.Icon, namesgenerator.GetRandomName(1)), + Options: takeFirstSlice(orig.Options, []byte("[]")), + ValidationRegex: takeFirst(orig.ValidationRegex, ""), + ValidationMin: takeFirst(orig.ValidationMin, sql.NullInt32{}), + ValidationMax: takeFirst(orig.ValidationMax, sql.NullInt32{}), + ValidationError: takeFirst(orig.ValidationError, ""), + ValidationMonotonic: takeFirst(orig.ValidationMonotonic, ""), + Required: takeFirst(orig.Required, false), + DisplayName: takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1)), + DisplayOrder: takeFirst(orig.DisplayOrder, 0), + Ephemeral: takeFirst(orig.Ephemeral, false), }) require.NoError(t, err, "insert template version parameter") return version @@ -656,24 +653,24 @@ func WorkspaceAgentStat(t testing.TB, db database.Store, orig database.Workspace if orig.ConnectionsByProto == nil { orig.ConnectionsByProto = json.RawMessage([]byte("{}")) } - scheme, err := db.InsertWorkspaceAgentStat(Ctx, database.InsertWorkspaceAgentStatParams{ - ID: TakeFirst(orig.ID, uuid.New()), - CreatedAt: TakeFirst(orig.CreatedAt, dbtime.Now()), - UserID: TakeFirst(orig.UserID, uuid.New()), - TemplateID: TakeFirst(orig.TemplateID, uuid.New()), - WorkspaceID: TakeFirst(orig.WorkspaceID, uuid.New()), - AgentID: TakeFirst(orig.AgentID, uuid.New()), + scheme, err := db.InsertWorkspaceAgentStat(genCtx, database.InsertWorkspaceAgentStatParams{ + ID: takeFirst(orig.ID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UserID: takeFirst(orig.UserID, uuid.New()), + TemplateID: takeFirst(orig.TemplateID, uuid.New()), + WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()), + AgentID: takeFirst(orig.AgentID, uuid.New()), ConnectionsByProto: orig.ConnectionsByProto, - ConnectionCount: TakeFirst(orig.ConnectionCount, 0), - RxPackets: TakeFirst(orig.RxPackets, 0), - RxBytes: TakeFirst(orig.RxBytes, 0), - TxPackets: TakeFirst(orig.TxPackets, 0), - TxBytes: TakeFirst(orig.TxBytes, 0), - SessionCountVSCode: TakeFirst(orig.SessionCountVSCode, 0), - SessionCountJetBrains: TakeFirst(orig.SessionCountJetBrains, 0), - SessionCountReconnectingPTY: TakeFirst(orig.SessionCountReconnectingPTY, 0), - SessionCountSSH: TakeFirst(orig.SessionCountSSH, 0), - ConnectionMedianLatencyMS: TakeFirst(orig.ConnectionMedianLatencyMS, 0), + ConnectionCount: takeFirst(orig.ConnectionCount, 0), + RxPackets: takeFirst(orig.RxPackets, 0), + RxBytes: takeFirst(orig.RxBytes, 0), + TxPackets: takeFirst(orig.TxPackets, 0), + TxBytes: takeFirst(orig.TxBytes, 0), + SessionCountVSCode: takeFirst(orig.SessionCountVSCode, 0), + SessionCountJetBrains: takeFirst(orig.SessionCountJetBrains, 0), + SessionCountReconnectingPTY: takeFirst(orig.SessionCountReconnectingPTY, 0), + SessionCountSSH: takeFirst(orig.SessionCountSSH, 0), + ConnectionMedianLatencyMS: takeFirst(orig.ConnectionMedianLatencyMS, 0), }) require.NoError(t, err, "insert workspace agent stat") return scheme @@ -692,9 +689,9 @@ func takeFirstIP(values ...net.IPNet) net.IPNet { }) } -// TakeFirstSlice implements takeFirst for []any. +// takeFirstSlice implements takeFirst for []any. // []any is not a comparable type. -func TakeFirstSlice[T any](values ...[]T) []T { +func takeFirstSlice[T any](values ...[]T) []T { return takeFirstF(values, func(v []T) bool { return len(v) != 0 }) @@ -715,8 +712,8 @@ func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { return empty } -// TakeFirst will take the first non-empty value. -func TakeFirst[Value comparable](values ...Value) Value { +// takeFirst will take the first non-empty value. +func takeFirst[Value comparable](values ...Value) Value { var empty Value return takeFirstF(values, func(v Value) bool { return v != empty From 90d81fcfef10e4a81f7de2532e14cd34c8942fd5 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Thu, 30 Nov 2023 03:30:58 +0000 Subject: [PATCH 6/9] fix a test --- cli/configssh_test.go | 2 +- coderd/database/dbfake/dbfake.go | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cli/configssh_test.go b/cli/configssh_test.go index 704c74113e14e..c338b5ca7ef4b 100644 --- a/cli/configssh_test.go +++ b/cli/configssh_test.go @@ -698,7 +698,7 @@ func TestConfigSSH_Hostnames(t *testing.T) { r := dbfake.Workspace(t, db).Seed(database.Workspace{ OrganizationID: owner.OrganizationID, OwnerID: memberUser.ID, - }).Do() + }).Resources(resources...).Do() dbfake.WorkspaceBuild(t, db, r.Workspace).Resource(resources...).Do() sshConfigFile := sshConfigFileName(t) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 63a678b6a3dd6..f7416732d1cc8 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -124,6 +124,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { // No ID on the workspace implies we should generate an entry. if b.ws.ID == uuid.Nil { + // nolint: revive b.ws = dbgen.Workspace(b.t, b.db, b.ws) resp.Workspace = b.ws } @@ -347,6 +348,11 @@ func (b WorkspaceBuilder) Seed(seed database.Workspace) WorkspaceBuilder { return b } +func (b WorkspaceBuilder) Resources(resources ...*sdkproto.Resource) WorkspaceBuilder { + b.resources = append(b.resources, resources...) + return b +} + func (b WorkspaceBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkproto.Agent) WorkspaceBuilder { //nolint: revive // returns modified struct b.agentToken = uuid.NewString() @@ -376,10 +382,11 @@ func (b WorkspaceBuilder) Do() WorkspaceResponse { // Tests can provide a custom template ID if necessary. var versionID uuid.UUID if b.seed.TemplateID == uuid.Nil { - resp := TemplateVersion(b.t, b.db).Seed(database.TemplateVersion{ - OrganizationID: b.seed.OrganizationID, - CreatedBy: b.seed.OwnerID, - }).Do() + resp := TemplateVersion(b.t, b.db). + Seed(database.TemplateVersion{ + OrganizationID: b.seed.OrganizationID, + CreatedBy: b.seed.OwnerID, + }).Resources(b.resources...).Do() b.seed.TemplateID = resp.Template.ID r.TemplateVersionResponse = resp From bba579de8703ad8198298bda9bc469995e7386cf Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Thu, 30 Nov 2023 03:44:01 +0000 Subject: [PATCH 7/9] update with workspacebuilder --- cli/configssh_test.go | 5 ++--- coderd/workspaceagents_test.go | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cli/configssh_test.go b/cli/configssh_test.go index c338b5ca7ef4b..bc58ec5e2f5a2 100644 --- a/cli/configssh_test.go +++ b/cli/configssh_test.go @@ -695,11 +695,10 @@ func TestConfigSSH_Hostnames(t *testing.T) { owner := coderdtest.CreateFirstUser(t, client) member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: owner.OrganizationID, OwnerID: memberUser.ID, - }).Resources(resources...).Do() - dbfake.WorkspaceBuild(t, db, r.Workspace).Resource(resources...).Do() + }).Resource(resources...).Do() sshConfigFile := sshConfigFileName(t) inv, root := clitest.New(t, "config-ssh", "--ssh-config-file", sshConfigFile) diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index 24c6e94fb89fe..ef6e71f50a7c8 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -47,7 +47,7 @@ func TestWorkspaceAgent(t *testing.T) { tmpDir := t.TempDir() anotherClient, anotherUser := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: anotherUser.ID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { From 958a0fef086644743a2c38b60d83757f89d5c937 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Fri, 1 Dec 2023 00:22:05 +0000 Subject: [PATCH 8/9] remove workspacebuilder --- cli/agent_test.go | 26 +++---- cli/configssh_test.go | 13 ++-- cli/gitssh_test.go | 12 ++- cli/list_test.go | 6 +- cli/portforward_test.go | 5 +- cli/schedule_test.go | 9 ++- cli/ssh_test.go | 15 ++-- coderd/database/dbfake/dbfake.go | 122 ++++++++----------------------- coderd/workspaceagents_test.go | 38 +++++----- 9 files changed, 87 insertions(+), 159 deletions(-) diff --git a/cli/agent_test.go b/cli/agent_test.go index f69426bd319c7..eb0140c44631d 100644 --- a/cli/agent_test.go +++ b/cli/agent_test.go @@ -31,11 +31,10 @@ func TestWorkspaceAgent(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db). - Seed(database.Workspace{ - OrganizationID: user.OrganizationID, - OwnerID: user.UserID, - }). + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ + OrganizationID: user.OrganizationID, + OwnerID: user.UserID, + }). WithAgent(). Do() logDir := t.TempDir() @@ -68,7 +67,7 @@ func TestWorkspaceAgent(t *testing.T) { AzureCertificates: certificates, }) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -105,7 +104,7 @@ func TestWorkspaceAgent(t *testing.T) { AWSCertificates: certificates, }) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -143,7 +142,7 @@ func TestWorkspaceAgent(t *testing.T) { }) owner := coderdtest.CreateFirstUser(t, client) member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: owner.OrganizationID, OwnerID: memberUser.ID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -195,13 +194,10 @@ func TestWorkspaceAgent(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db). - Seed(database.Workspace{ - OrganizationID: user.OrganizationID, - OwnerID: user.UserID, - }). - WithAgent(). - Do() + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ + OrganizationID: user.OrganizationID, + OwnerID: user.UserID, + }).WithAgent().Do() logDir := t.TempDir() inv, _ := clitest.New(t, diff --git a/cli/configssh_test.go b/cli/configssh_test.go index bc58ec5e2f5a2..d87d1fa7024e6 100644 --- a/cli/configssh_test.go +++ b/cli/configssh_test.go @@ -77,13 +77,10 @@ func TestConfigSSH(t *testing.T) { }) owner := coderdtest.CreateFirstUser(t, client) member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - r := dbfake.Workspace(t, db). - Seed(database.Workspace{ - OrganizationID: owner.OrganizationID, - OwnerID: memberUser.ID, - }). - WithAgent(). - Do() + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ + OrganizationID: owner.OrganizationID, + OwnerID: memberUser.ID, + }).WithAgent().Do() _ = agenttest.New(t, client.URL, r.AgentToken) resources := coderdtest.AwaitWorkspaceAgents(t, client, r.Workspace.ID) agentConn, err := client.DialWorkspaceAgent(context.Background(), resources[0].Agents[0].ID, nil) @@ -575,7 +572,7 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) if tt.hasAgent { - _ = dbfake.Workspace(t, db).Seed(database.Workspace{ + _ = dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() diff --git a/cli/gitssh_test.go b/cli/gitssh_test.go index 20cf1ecbecaa4..83b873dec914e 100644 --- a/cli/gitssh_test.go +++ b/cli/gitssh_test.go @@ -48,13 +48,11 @@ func prepareTestGitSSH(ctx context.Context, t *testing.T) (*agentsdk.Client, str require.NoError(t, err) // setup template - r := dbfake.Workspace(t, db). - Seed(database.Workspace{ - OrganizationID: user.OrganizationID, - OwnerID: user.UserID, - }). - WithAgent(). - Do() + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ + OrganizationID: user.OrganizationID, + OwnerID: user.UserID, + }).WithAgent().Do() + // start workspace agent agentClient := agentsdk.New(client.URL) agentClient.SetSessionToken(r.AgentToken) diff --git a/cli/list_test.go b/cli/list_test.go index 7836bde5d5453..82d372bd350aa 100644 --- a/cli/list_test.go +++ b/cli/list_test.go @@ -25,10 +25,12 @@ func TestList(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) owner := coderdtest.CreateFirstUser(t, client) member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + // setup template + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: owner.OrganizationID, OwnerID: memberUser.ID, }).WithAgent().Do() + inv, root := clitest.New(t, "ls") clitest.SetupConfig(t, member, root) pty := ptytest.New(t).Attach(inv) @@ -52,7 +54,7 @@ func TestList(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) owner := coderdtest.CreateFirstUser(t, client) member, memberUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) - dbfake.Workspace(t, db).Seed(database.Workspace{ + _ = dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: owner.OrganizationID, OwnerID: memberUser.ID, }).WithAgent().Do() diff --git a/cli/portforward_test.go b/cli/portforward_test.go index 80b1ef28c13cd..d357e5c371ddf 100644 --- a/cli/portforward_test.go +++ b/cli/portforward_test.go @@ -305,10 +305,11 @@ func runAgent(t *testing.T, client *codersdk.Client, owner uuid.UUID, db databas require.NoError(t, err, "specified user does not exist") require.Greater(t, len(user.OrganizationIDs), 0, "user has no organizations") orgID := user.OrganizationIDs[0] - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: orgID, - OwnerID: owner, + OwnerID: user.ID, }).WithAgent().Do() + _ = agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) { o.SSHMaxTimeout = 60 * time.Second diff --git a/cli/schedule_test.go b/cli/schedule_test.go index 591ff2aa8d395..9ed44de9e467f 100644 --- a/cli/schedule_test.go +++ b/cli/schedule_test.go @@ -38,26 +38,27 @@ func setupTestSchedule(t *testing.T, sched *cron.Schedule) (ownerClient, memberC memberClient, memberUser := coderdtest.CreateAnotherUserMutators(t, ownerClient, owner.OrganizationID, nil, func(r *codersdk.CreateUserRequest) { r.Username = "testuser2" // ensure deterministic ordering }) - _ = dbfake.Workspace(t, db).Seed(database.Workspace{ + _ = dbfake.WorkspaceBuild(t, db, database.Workspace{ Name: "a-owner", OwnerID: owner.UserID, OrganizationID: owner.OrganizationID, AutostartSchedule: sql.NullString{String: sched.String(), Valid: true}, Ttl: sql.NullInt64{Int64: 8 * time.Hour.Nanoseconds(), Valid: true}, }).WithAgent().Do() - _ = dbfake.Workspace(t, db).Seed(database.Workspace{ + + _ = dbfake.WorkspaceBuild(t, db, database.Workspace{ Name: "b-owner", OwnerID: owner.UserID, OrganizationID: owner.OrganizationID, AutostartSchedule: sql.NullString{String: sched.String(), Valid: true}, }).WithAgent().Do() - _ = dbfake.Workspace(t, db).Seed(database.Workspace{ + _ = dbfake.WorkspaceBuild(t, db, database.Workspace{ Name: "c-member", OwnerID: memberUser.ID, OrganizationID: owner.OrganizationID, Ttl: sql.NullInt64{Int64: 8 * time.Hour.Nanoseconds(), Valid: true}, }).WithAgent().Do() - _ = dbfake.Workspace(t, db).Seed(database.Workspace{ + _ = dbfake.WorkspaceBuild(t, db, database.Workspace{ Name: "d-member", OwnerID: memberUser.ID, OrganizationID: owner.OrganizationID, diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 7ee04b50cfc62..e60117ee6f555 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -52,13 +52,10 @@ func setupWorkspaceForAgent(t *testing.T, mutations ...func([]*proto.Agent) []*p client.SetLogger(slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug)) first := coderdtest.CreateFirstUser(t, client) userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID) - r := dbfake.Workspace(t, store). - Seed(database.Workspace{ - OrganizationID: first.OrganizationID, - OwnerID: user.ID, - }). - WithAgent(mutations...). - Do() + r := dbfake.WorkspaceBuild(t, store, database.Workspace{ + OrganizationID: first.OrganizationID, + OwnerID: user.ID, + }).WithAgent(mutations...).Do() return userClient, r.Workspace, r.AgentToken } @@ -130,7 +127,7 @@ func TestSSH(t *testing.T) { client.SetLogger(slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug)) first := coderdtest.CreateFirstUser(t, client) userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID) - r := dbfake.Workspace(t, store).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, store, database.Workspace{ OrganizationID: first.OrganizationID, OwnerID: user.ID, }).WithAgent().Do() @@ -469,7 +466,7 @@ func TestSSH(t *testing.T) { client.SetLogger(slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug)) first := coderdtest.CreateFirstUser(t, client) userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID) - r := dbfake.Workspace(t, store).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, store, database.Workspace{ OrganizationID: first.OrganizationID, OwnerID: user.ID, }).WithAgent().Do() diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index f7416732d1cc8..4cac09d1dc44f 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -22,7 +22,7 @@ import ( sdkproto "github.com/coder/coder/v2/provisionersdk/proto" ) -var sysCtx = dbauthz.As(context.Background(), rbac.Subject{ +var ownerCtx = dbauthz.As(context.Background(), rbac.Subject{ ID: "owner", Roles: rbac.Roles(must(rbac.RoleNames{rbac.RoleOwner()}.Expand())), Groups: []string{}, @@ -36,6 +36,8 @@ type WorkspaceResponse struct { TemplateVersionResponse } +// WorkspaceBuildBuilder generates workspace builds and associated +// resources. type WorkspaceBuildBuilder struct { t testing.TB db database.Store @@ -47,6 +49,10 @@ type WorkspaceBuildBuilder struct { agentToken string } +// WorkspaceBuild generates a workspace build for the provided workspace. +// Pass a database.Workspace{} with a nil ID to also generate a new workspace. +// Omitting the template ID on a workspace will also generate a new template +// with a template version. func WorkspaceBuild(t testing.TB, db database.Store, ws database.Workspace) WorkspaceBuildBuilder { return WorkspaceBuildBuilder{t: t, db: db, ws: ws} } @@ -94,13 +100,20 @@ func (b WorkspaceBuildBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) [] return b } +// Do generates all the resources associated with a workspace build. +// Template and TemplateVersion will be optionally populated if no +// TemplateID is set on the provided workspace. +// Workspace will be optionally populated if no ID is set on the provided +// workspace. func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { b.t.Helper() jobID := uuid.New() b.seed.ID = uuid.New() b.seed.JobID = jobID - var resp WorkspaceResponse + resp := WorkspaceResponse{ + AgentToken: b.agentToken, + } if b.ws.TemplateID == uuid.Nil { resp.TemplateVersionResponse = TemplateVersion(b.t, b.db). Resources(b.resources...). @@ -116,7 +129,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { // If no template version is set assume the active version. if b.seed.TemplateVersionID == uuid.Nil { - template, err := b.db.GetTemplateByID(sysCtx, b.ws.TemplateID) + template, err := b.db.GetTemplateByID(ownerCtx, b.ws.TemplateID) require.NoError(b.t, err) require.NotNil(b.t, template.ActiveVersionID, "active version ID unexpectedly nil") b.seed.TemplateVersionID = template.ActiveVersionID @@ -129,6 +142,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { resp.Workspace = b.ws } b.seed.WorkspaceID = b.ws.ID + b.seed.InitiatorID = takeFirst(b.seed.InitiatorID, b.ws.OwnerID) // Create a provisioner job for the build! payload, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{ @@ -136,7 +150,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { }) require.NoError(b.t, err) - job, err := b.db.InsertProvisionerJob(sysCtx, database.InsertProvisionerJobParams{ + job, err := b.db.InsertProvisionerJob(ownerCtx, database.InsertProvisionerJobParams{ ID: jobID, CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), @@ -152,7 +166,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { }) require.NoError(b.t, err, "insert job") - err = b.db.UpdateProvisionerJobWithCompleteByID(sysCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ + err = b.db.UpdateProvisionerJobWithCompleteByID(ownerCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: job.ID, UpdatedAt: dbtime.Now(), Error: sql.NullString{}, @@ -166,15 +180,17 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { resp.Build = dbgen.WorkspaceBuild(b.t, b.db, b.seed) ProvisionerJobResources(b.t, b.db, job.ID, b.seed.Transition, b.resources...).Do() - if b.ps != nil { - err = b.ps.Publish(codersdk.WorkspaceNotifyChannel(resp.Build.WorkspaceID), []byte{}) - require.NoError(b.t, err) - } for i := range b.params { b.params[i].WorkspaceBuildID = resp.Build.ID } _ = dbgen.WorkspaceBuildParameters(b.t, b.db, b.params) + + if b.ps != nil { + err = b.ps.Publish(codersdk.WorkspaceNotifyChannel(resp.Build.WorkspaceID), []byte{}) + require.NoError(b.t, err) + } + return resp } @@ -208,7 +224,7 @@ func (b ProvisionerJobResourcesBuilder) Do() { } for _, resource := range b.resources { //nolint:gocritic // This is only used by tests. - err := provisionerdserver.InsertWorkspaceResource(sysCtx, b.db, b.jobID, transition, resource, &telemetry.Snapshot{}) + err := provisionerdserver.InsertWorkspaceResource(ownerCtx, b.db, b.jobID, transition, resource, &telemetry.Snapshot{}) require.NoError(b.t, err) } } @@ -228,6 +244,8 @@ type TemplateVersionBuilder struct { promote bool } +// TemplateVersion generates a template version and optionally a parent +// template if no template ID is set on the seed. func TemplateVersion(t testing.TB, db database.Store) TemplateVersionBuilder { return TemplateVersionBuilder{ t: t, @@ -285,7 +303,7 @@ func (t TemplateVersionBuilder) Do() TemplateVersionResponse { // Always make this version the active version. We can easily // add a conditional to the builder to opt out of this when // necessary. - err := t.db.UpdateTemplateActiveVersionByID(sysCtx, database.UpdateTemplateActiveVersionByIDParams{ + err := t.db.UpdateTemplateActiveVersionByID(ownerCtx, database.UpdateTemplateActiveVersionByIDParams{ ID: t.seed.TemplateID.UUID, ActiveVersionID: t.seed.ID, UpdatedAt: dbtime.Now(), @@ -329,88 +347,6 @@ func must[V any](v V, err error) V { return v } -// TODO update existing callers to call WorkspaceBuildBuilder then delete below. -type WorkspaceBuilder struct { - t testing.TB - db database.Store - seed database.Workspace - resources []*sdkproto.Resource - agentToken string -} - -func Workspace(t testing.TB, db database.Store) WorkspaceBuilder { - return WorkspaceBuilder{t: t, db: db} -} - -func (b WorkspaceBuilder) Seed(seed database.Workspace) WorkspaceBuilder { - //nolint: revive // returns modified struct - b.seed = seed - return b -} - -func (b WorkspaceBuilder) Resources(resources ...*sdkproto.Resource) WorkspaceBuilder { - b.resources = append(b.resources, resources...) - return b -} - -func (b WorkspaceBuilder) WithAgent(mutations ...func([]*sdkproto.Agent) []*sdkproto.Agent) WorkspaceBuilder { - //nolint: revive // returns modified struct - b.agentToken = uuid.NewString() - agents := []*sdkproto.Agent{{ - Id: uuid.NewString(), - Auth: &sdkproto.Agent_Token{ - Token: b.agentToken, - }, - }} - for _, m := range mutations { - agents = m(agents) - } - b.resources = append(b.resources, &sdkproto.Resource{ - Name: "example", - Type: "aws_instance", - Agents: agents, - }) - return b -} - -func (b WorkspaceBuilder) Do() WorkspaceResponse { - b.t.Helper() - - var r WorkspaceResponse - - // This intentionally fulfills the minimum requirements of the schema. - // Tests can provide a custom template ID if necessary. - var versionID uuid.UUID - if b.seed.TemplateID == uuid.Nil { - resp := TemplateVersion(b.t, b.db). - Seed(database.TemplateVersion{ - OrganizationID: b.seed.OrganizationID, - CreatedBy: b.seed.OwnerID, - }).Resources(b.resources...).Do() - - b.seed.TemplateID = resp.Template.ID - r.TemplateVersionResponse = resp - versionID = resp.TemplateVersion.ID - } else { - template, err := b.db.GetTemplateByID(sysCtx, b.seed.TemplateID) - require.NoError(b.t, err) - require.NotNil(b.t, template.ActiveVersionID, "active version unexpectedly nil") - versionID = template.ActiveVersionID - } - - r.Workspace = dbgen.Workspace(b.t, b.db, b.seed) - if b.agentToken != "" { - r.AgentToken = b.agentToken - r.Build = WorkspaceBuild(b.t, b.db, r.Workspace). - Resource(b.resources...). - Seed(database.WorkspaceBuild{ - TemplateVersionID: versionID, - }). - Do().Build - } - return r -} - // takeFirstF takes the first value that returns true func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { for _, v := range values { diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index ef6e71f50a7c8..0b6d6316b03a8 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -69,7 +69,7 @@ func TestWorkspaceAgent(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) tmpDir := t.TempDir() - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -97,7 +97,7 @@ func TestWorkspaceAgent(t *testing.T) { wantTroubleshootingURL := "https://example.com/troubleshoot" - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -138,7 +138,7 @@ func TestWorkspaceAgent(t *testing.T) { PortForwardingHelper: true, SshHelper: true, } - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -171,7 +171,7 @@ func TestWorkspaceAgent(t *testing.T) { apps.WebTerminal = false // Creating another workspace is easier - r = dbfake.Workspace(t, db).Seed(database.Workspace{ + r = dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -195,7 +195,7 @@ func TestWorkspaceAgentLogs(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -237,7 +237,7 @@ func TestWorkspaceAgentLogs(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -279,7 +279,7 @@ func TestWorkspaceAgentLogs(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -322,7 +322,7 @@ func TestWorkspaceAgentListen(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -408,7 +408,7 @@ func TestWorkspaceAgentTailnet(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -454,7 +454,7 @@ func TestWorkspaceAgentTailnetDirectDisabled(t *testing.T) { DeploymentValues: dv, }) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -517,7 +517,7 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) { require.NoError(t, err) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -752,7 +752,7 @@ func TestWorkspaceAgentAppHealth(t *testing.T) { }, }, } - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -819,7 +819,7 @@ func TestWorkspaceAgentReportStats(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -886,7 +886,7 @@ func TestWorkspaceAgent_LifecycleState(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -951,7 +951,7 @@ func TestWorkspaceAgent_Metadata(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -1129,7 +1129,7 @@ func TestWorkspaceAgent_Metadata_CatchMemoryLeak(t *testing.T) { Logger: &logger, }) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { @@ -1258,7 +1258,7 @@ func TestWorkspaceAgent_Startup(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -1304,7 +1304,7 @@ func TestWorkspaceAgent_Startup(t *testing.T) { client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) - r := dbfake.Workspace(t, db).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() @@ -1352,7 +1352,7 @@ func TestWorkspaceAgent_UpdatedDERP(t *testing.T) { api.DERPMapper.Store(&derpMapFn) // Start workspace a workspace agent. - r := dbfake.Workspace(t, api.Database).Seed(database.Workspace{ + r := dbfake.WorkspaceBuild(t, api.Database, database.Workspace{ OrganizationID: user.OrganizationID, OwnerID: user.UserID, }).WithAgent().Do() From 4dfd3be8385521c1066d5fa6441bb94dcd55baa9 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Fri, 1 Dec 2023 00:44:56 +0000 Subject: [PATCH 9/9] bad copy pasta --- cli/portforward_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/portforward_test.go b/cli/portforward_test.go index d357e5c371ddf..38971a0c89ba4 100644 --- a/cli/portforward_test.go +++ b/cli/portforward_test.go @@ -307,7 +307,7 @@ func runAgent(t *testing.T, client *codersdk.Client, owner uuid.UUID, db databas orgID := user.OrganizationIDs[0] r := dbfake.WorkspaceBuild(t, db, database.Workspace{ OrganizationID: orgID, - OwnerID: user.ID, + OwnerID: owner, }).WithAgent().Do() _ = agenttest.New(t, client.URL, r.AgentToken,