diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fbc34b0dfb6ec..571e7ce15580c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -490,6 +490,12 @@ jobs: gotestsum --format standard-quiet --packages "$PACKAGES" \ -- -timeout=20m -v -p $NUM_PARALLEL_PACKAGES -parallel=$NUM_PARALLEL_TESTS $TESTCOUNT + - name: Upload failed test db dumps + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: failed-test-db-dump-${{matrix.os}} + path: "**/*.test.sql" + - name: Upload Go Build Cache uses: ./.github/actions/test-cache/upload with: diff --git a/cli/agent_test.go b/cli/agent_test.go index 0a948c0c84e9a..1592235babaef 100644 --- a/cli/agent_test.go +++ b/cli/agent_test.go @@ -21,6 +21,7 @@ import ( "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/database/dbtestutil" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/workspacesdk" "github.com/coder/coder/v2/provisionersdk/proto" @@ -67,7 +68,12 @@ func TestWorkspaceAgent(t *testing.T) { t.Parallel() instanceID := "instanceidentifier" certificates, metadataClient := coderdtest.NewAzureInstanceIdentity(t, instanceID) - client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + db, ps := dbtestutil.NewDB(t, + dbtestutil.WithDumpOnFailure(), + ) + client := coderdtest.New(t, &coderdtest.Options{ + Database: db, + Pubsub: ps, AzureCertificates: certificates, }) user := coderdtest.CreateFirstUser(t, client) @@ -106,7 +112,12 @@ func TestWorkspaceAgent(t *testing.T) { t.Parallel() instanceID := "instanceidentifier" certificates, metadataClient := coderdtest.NewAWSInstanceIdentity(t, instanceID) - client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + db, ps := dbtestutil.NewDB(t, + dbtestutil.WithDumpOnFailure(), + ) + client := coderdtest.New(t, &coderdtest.Options{ + Database: db, + Pubsub: ps, AWSCertificates: certificates, }) user := coderdtest.CreateFirstUser(t, client) @@ -146,7 +157,12 @@ func TestWorkspaceAgent(t *testing.T) { t.Parallel() instanceID := "instanceidentifier" validator, metadataClient := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false) - client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + db, ps := dbtestutil.NewDB(t, + dbtestutil.WithDumpOnFailure(), + ) + client := coderdtest.New(t, &coderdtest.Options{ + Database: db, + Pubsub: ps, GoogleTokenValidator: validator, }) owner := coderdtest.CreateFirstUser(t, client) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index e7c00255d47c2..6d99005fb3334 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -12,6 +12,9 @@ import ( "github.com/sqlc-dev/pqtype" "github.com/stretchr/testify/require" + "cdr.dev/slog" + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbgen" @@ -43,6 +46,7 @@ type WorkspaceResponse struct { // resources. type WorkspaceBuildBuilder struct { t testing.TB + logger slog.Logger db database.Store ps pubsub.Pubsub ws database.WorkspaceTable @@ -62,7 +66,10 @@ type workspaceBuildDisposition struct { // 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.WorkspaceTable) WorkspaceBuildBuilder { - return WorkspaceBuildBuilder{t: t, db: db, ws: ws} + return WorkspaceBuildBuilder{ + t: t, db: db, ws: ws, + logger: slogtest.Make(t, &slogtest.Options{}).Named("dbfake").Leveled(slog.LevelDebug), + } } func (b WorkspaceBuildBuilder) Pubsub(ps pubsub.Pubsub) WorkspaceBuildBuilder { @@ -131,6 +138,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { AgentToken: b.agentToken, } if b.ws.TemplateID == uuid.Nil { + b.logger.Debug(context.Background(), "creating template and version") resp.TemplateVersionResponse = TemplateVersion(b.t, b.db). Resources(b.resources...). Pubsub(b.ps). @@ -145,6 +153,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { // If no template version is set assume the active version. if b.seed.TemplateVersionID == uuid.Nil { + b.logger.Debug(context.Background(), "assuming active template version") 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") @@ -156,6 +165,9 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { // nolint: revive b.ws = dbgen.Workspace(b.t, b.db, b.ws) resp.Workspace = b.ws + b.logger.Debug(context.Background(), "created workspace", + slog.F("name", resp.Workspace.Name), + slog.F("workspace_id", resp.Workspace.ID)) } b.seed.WorkspaceID = b.ws.ID b.seed.InitiatorID = takeFirst(b.seed.InitiatorID, b.ws.OwnerID) @@ -182,10 +194,12 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { LogsOverflowed: false, }) require.NoError(b.t, err, "insert job") + b.logger.Debug(context.Background(), "inserted provisioner job", slog.F("job_id", job.ID)) if b.dispo.starting { // might need to do this multiple times if we got a template version // import job as well + b.logger.Debug(context.Background(), "looping to acquire provisioner job") for { j, err := b.db.AcquireProvisionerJob(ownerCtx, database.AcquireProvisionerJobParams{ OrganizationID: job.OrganizationID, @@ -202,10 +216,12 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { }) require.NoError(b.t, err, "acquire starting job") if j.ID == job.ID { + b.logger.Debug(context.Background(), "acquired provisioner job", slog.F("job_id", job.ID)) break } } } else { + b.logger.Debug(context.Background(), "completing the provisioner job") err = b.db.UpdateProvisionerJobWithCompleteByID(ownerCtx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: job.ID, UpdatedAt: dbtime.Now(), @@ -221,11 +237,16 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { } resp.Build = dbgen.WorkspaceBuild(b.t, b.db, b.seed) + b.logger.Debug(context.Background(), "created workspace build", + slog.F("build_id", resp.Build.ID), + slog.F("workspace_id", resp.Workspace.ID), + slog.F("build_number", resp.Build.BuildNumber)) for i := range b.params { b.params[i].WorkspaceBuildID = resp.Build.ID } - _ = dbgen.WorkspaceBuildParameters(b.t, b.db, b.params) + params := dbgen.WorkspaceBuildParameters(b.t, b.db, b.params) + b.logger.Debug(context.Background(), "created workspace build parameters", slog.F("count", len(params))) if b.ws.Deleted { err = b.db.UpdateWorkspaceDeletedByID(ownerCtx, database.UpdateWorkspaceDeletedByIDParams{ @@ -233,6 +254,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { Deleted: true, }) require.NoError(b.t, err) + b.logger.Debug(context.Background(), "deleted workspace", slog.F("workspace_id", resp.Workspace.ID)) } if b.ps != nil { @@ -243,6 +265,9 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { require.NoError(b.t, err) err = b.ps.Publish(wspubsub.WorkspaceEventChannel(resp.Workspace.OwnerID), msg) require.NoError(b.t, err) + b.logger.Debug(context.Background(), "published workspace event", + slog.F("owner_id", resp.Workspace.ID), + slog.F("owner_id", resp.Workspace.OwnerID)) } agents, err := b.db.GetWorkspaceAgentsByWorkspaceAndBuildNumber(ownerCtx, database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{ @@ -260,7 +285,12 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { err = b.db.DeleteWorkspaceSubAgentByID(ownerCtx, subAgent.ID) require.NoError(b.t, err, "delete workspace agent subagent antagonist") - b.t.Logf("inserted deleted subagent antagonist %s (%v) for workspace agent %s (%v)", subAgent.Name, subAgent.ID, agent.Name, agent.ID) + b.logger.Debug(context.Background(), "inserted deleted subagent antagonist", + slog.F("subagent_name", subAgent.Name), + slog.F("subagent_id", subAgent.ID), + slog.F("agent_name", agent.Name), + slog.F("agent_id", agent.ID), + ) } } @@ -269,6 +299,7 @@ func (b WorkspaceBuildBuilder) Do() WorkspaceResponse { type ProvisionerJobResourcesBuilder struct { t testing.TB + logger slog.Logger db database.Store jobID uuid.UUID transition database.WorkspaceTransition @@ -281,6 +312,7 @@ func ProvisionerJobResources( ) ProvisionerJobResourcesBuilder { return ProvisionerJobResourcesBuilder{ t: t, + logger: slogtest.Make(t, &slogtest.Options{}).Named("dbfake").Leveled(slog.LevelDebug).With(slog.F("job_id", jobID)), db: db, jobID: jobID, transition: transition, @@ -292,13 +324,17 @@ func (b ProvisionerJobResourcesBuilder) Do() { b.t.Helper() transition := b.transition if transition == "" { - // Default to start! + b.logger.Debug(context.Background(), "setting default transition to start") transition = database.WorkspaceTransitionStart } for _, resource := range b.resources { //nolint:gocritic // This is only used by tests. err := provisionerdserver.InsertWorkspaceResource(ownerCtx, b.db, b.jobID, transition, resource, &telemetry.Snapshot{}) require.NoError(b.t, err) + b.logger.Debug(context.Background(), "created workspace resource", + slog.F("resource_name", resource.Name), + slog.F("agent_count", len(resource.Agents)), + ) } } @@ -309,6 +345,7 @@ type TemplateVersionResponse struct { type TemplateVersionBuilder struct { t testing.TB + logger slog.Logger db database.Store seed database.TemplateVersion fileID uuid.UUID @@ -326,6 +363,7 @@ type TemplateVersionBuilder struct { func TemplateVersion(t testing.TB, db database.Store) TemplateVersionBuilder { return TemplateVersionBuilder{ t: t, + logger: slogtest.Make(t, &slogtest.Options{}).Named("dbfake").Leveled(slog.LevelDebug), db: db, promote: true, autoCreateTemplate: true, @@ -396,9 +434,16 @@ func (t TemplateVersionBuilder) Do() TemplateVersionResponse { Valid: true, UUID: resp.Template.ID, } + t.logger.Debug(context.Background(), "created template", + slog.F("organization_id", resp.Template.OrganizationID), + slog.F("template_id", resp.Template.CreatedBy), + ) } version := dbgen.TemplateVersion(t.t, t.db, t.seed) + t.logger.Debug(context.Background(), "created template version", + slog.F("template_version_id", version.ID), + ) if t.promote { err := t.db.UpdateTemplateActiveVersionByID(ownerCtx, database.UpdateTemplateActiveVersionByIDParams{ ID: t.seed.TemplateID.UUID, @@ -406,10 +451,13 @@ func (t TemplateVersionBuilder) Do() TemplateVersionResponse { UpdatedAt: dbtime.Now(), }) require.NoError(t.t, err) + t.logger.Debug(context.Background(), "promoted template version", + slog.F("template_version_id", t.seed.ID), + ) } for _, preset := range t.presets { - dbgen.Preset(t.t, t.db, database.InsertPresetParams{ + prst := dbgen.Preset(t.t, t.db, database.InsertPresetParams{ ID: preset.ID, TemplateVersionID: version.ID, Name: preset.Name, @@ -421,14 +469,19 @@ func (t TemplateVersionBuilder) Do() TemplateVersionResponse { Description: preset.Description, Icon: preset.Icon, }) + t.logger.Debug(context.Background(), "added preset", + slog.F("preset_id", prst.ID), + slog.F("preset_name", prst.Name), + ) } for _, presetParam := range t.presetParams { - dbgen.PresetParameter(t.t, t.db, database.InsertPresetParametersParams{ + prm := dbgen.PresetParameter(t.t, t.db, database.InsertPresetParametersParams{ TemplateVersionPresetID: presetParam.TemplateVersionPresetID, Names: []string{presetParam.Name}, Values: []string{presetParam.Value}, }) + t.logger.Debug(context.Background(), "added preset parameter", slog.F("param_name", prm[0].Name)) } payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{ @@ -448,6 +501,7 @@ func (t TemplateVersionBuilder) Do() TemplateVersionResponse { }, FileID: t.fileID, }) + t.logger.Debug(context.Background(), "added template version import job", slog.F("job_id", job.ID)) t.seed.JobID = job.ID diff --git a/coderd/database/dbtestutil/db.go b/coderd/database/dbtestutil/db.go index f67e3206b09d1..4c7d7dcbf230e 100644 --- a/coderd/database/dbtestutil/db.go +++ b/coderd/database/dbtestutil/db.go @@ -206,7 +206,7 @@ func DumpOnFailure(t testing.TB, connectionURL string) { outPath := filepath.Join(cwd, snakeCaseName+"."+timeSuffix+".test.sql") dump, err := PGDump(connectionURL) if err != nil { - t.Errorf("dump on failure: failed to run pg_dump") + t.Errorf("dump on failure: failed to run pg_dump: %s", err.Error()) return } if err := os.WriteFile(outPath, normalizeDump(dump), 0o600); err != nil {
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: