Skip to content

fix(scaletest): fix flake in Test_Runner/Cleanup #10252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/exp_scaletest.go
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ func (r *runnableTraceWrapper) Run(ctx context.Context, id string, logs io.Write
return r.runner.Run(ctx2, id, logs)
}

func (r *runnableTraceWrapper) Cleanup(ctx context.Context, id string) error {
func (r *runnableTraceWrapper) Cleanup(ctx context.Context, id string, logs io.Writer) error {
c, ok := r.runner.(harness.Cleanable)
if !ok {
return nil
Expand All @@ -1253,7 +1253,7 @@ func (r *runnableTraceWrapper) Cleanup(ctx context.Context, id string) error {
ctx, span := r.tracer.Start(ctx, r.spanName+" cleanup")
defer span.End()

return c.Cleanup(ctx, id)
return c.Cleanup(ctx, id, logs)
}

// newScaleTestUser returns a random username and email address that can be used
Expand Down
6 changes: 4 additions & 2 deletions scaletest/createworkspaces/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,14 @@ resourceLoop:
}

// Cleanup implements Cleanable.
func (r *Runner) Cleanup(ctx context.Context, id string) error {
func (r *Runner) Cleanup(ctx context.Context, id string, logs io.Writer) error {
if r.cfg.NoCleanup {
_, _ = fmt.Fprintln(logs, "skipping cleanup")
return nil
}

if r.workspacebuildRunner != nil {
err := r.workspacebuildRunner.Cleanup(ctx, id)
err := r.workspacebuildRunner.Cleanup(ctx, id, logs)
if err != nil {
return xerrors.Errorf("cleanup workspace: %w", err)
}
Expand All @@ -191,6 +192,7 @@ func (r *Runner) Cleanup(ctx context.Context, id string) error {
if r.userID != uuid.Nil {
err := r.client.DeleteUser(ctx, r.userID)
if err != nil {
_, _ = fmt.Fprintf(logs, "failed to delete user %q: %v\n", r.userID.String(), err)
return xerrors.Errorf("delete user: %w", err)
}
}
Expand Down
34 changes: 27 additions & 7 deletions scaletest/createworkspaces/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,13 @@ func Test_Runner(t *testing.T) {
require.Contains(t, logsStr, "Opening reconnecting PTY connection to agent")
require.Contains(t, logsStr, "Opening connection to workspace agent")

err = runner.Cleanup(ctx, "1")
cleanupLogs := bytes.NewBuffer(nil)
err = runner.Cleanup(ctx, "1", cleanupLogs)
require.NoError(t, err)
cleanupLogsStr := cleanupLogs.String()
require.Contains(t, cleanupLogsStr, "deleting workspace")
require.NotContains(t, cleanupLogsStr, "canceling workspace build") // The build should have already completed.
require.Contains(t, cleanupLogsStr, "Build succeeded!")

// Ensure the user and workspace were deleted.
users, err = client.Users(ctx, codersdk.UsersRequest{})
Expand Down Expand Up @@ -217,7 +222,7 @@ func Test_Runner(t *testing.T) {
},
ProvisionApply: []*proto.Response{
{
Type: &proto.Response_Log{Log: &proto.Log{}},
Type: &proto.Response_Log{Log: &proto.Log{}}, // This provisioner job will never complete.
},
},
})
Expand Down Expand Up @@ -257,24 +262,36 @@ func Test_Runner(t *testing.T) {
close(done)
}()

// Wait for the workspace build job to be picked up.
require.Eventually(t, func() bool {
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{})
if err != nil {
return false
}
if len(workspaces.Workspaces) == 0 {
return false
}

return len(workspaces.Workspaces) > 0
}, testutil.WaitShort, testutil.IntervalFast)
ws := workspaces.Workspaces[0]
t.Logf("checking build: %s | %s", ws.LatestBuild.Transition, ws.LatestBuild.Job.Status)
// There should be only one build at present.
if ws.LatestBuild.Transition != codersdk.WorkspaceTransitionStart {
t.Errorf("expected build transition %s, got %s", codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition)
return false
}
return ws.LatestBuild.Job.Status == codersdk.ProvisionerJobRunning
}, testutil.WaitShort, testutil.IntervalMedium)

cancelFunc()
<-done

// When we run the cleanup, it should be canceled
cleanupLogs := bytes.NewBuffer(nil)
cancelCtx, cancelFunc = context.WithCancel(ctx)
done = make(chan struct{})
go func() {
// This will return an error as the "delete" operation will never complete.
_ = runner.Cleanup(cancelCtx, "1")
_ = runner.Cleanup(cancelCtx, "1", cleanupLogs)
close(done)
}()

Expand Down Expand Up @@ -311,9 +328,11 @@ func Test_Runner(t *testing.T) {
}
}
return false
}, testutil.WaitShort, testutil.IntervalFast)
}, testutil.WaitShort, testutil.IntervalMedium)
cancelFunc()
<-done
cleanupLogsStr := cleanupLogs.String()
require.Contains(t, cleanupLogsStr, "canceling workspace build")
})

t.Run("NoCleanup", func(t *testing.T) {
Expand Down Expand Up @@ -447,7 +466,8 @@ func Test_Runner(t *testing.T) {
require.Contains(t, logsStr, "Opening reconnecting PTY connection to agent")
require.Contains(t, logsStr, "Opening connection to workspace agent")

err = runner.Cleanup(ctx, "1")
cleanupLogs := bytes.NewBuffer(nil)
err = runner.Cleanup(ctx, "1", cleanupLogs)
require.NoError(t, err)

// Ensure the user and workspace were not deleted.
Expand Down
2 changes: 1 addition & 1 deletion scaletest/dashboard/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,6 @@ func (r *Runner) runUntilDeadlineExceeded(ctx context.Context) error {
}
}

func (*Runner) Cleanup(_ context.Context, _ string) error {
func (*Runner) Cleanup(_ context.Context, _ string, _ io.Writer) error {
return nil
}
6 changes: 3 additions & 3 deletions scaletest/harness/harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func Test_TestHarness(t *testing.T) {
RunFn: func(_ context.Context, _ string, _ io.Writer) error {
return nil
},
CleanupFn: func(_ context.Context, _ string) error {
CleanupFn: func(_ context.Context, _ string, _ io.Writer) error {
panic(testPanicMessage)
},
})
Expand Down Expand Up @@ -150,7 +150,7 @@ func Test_TestHarness(t *testing.T) {
RunFn: func(_ context.Context, _ string, _ io.Writer) error {
return nil
},
CleanupFn: func(_ context.Context, _ string) error {
CleanupFn: func(_ context.Context, _ string, _ io.Writer) error {
return nil
},
})
Expand Down Expand Up @@ -295,7 +295,7 @@ func fakeTestFns(err, cleanupErr error) testFns {
RunFn: func(_ context.Context, _ string, _ io.Writer) error {
return err
},
CleanupFn: func(_ context.Context, _ string) error {
CleanupFn: func(_ context.Context, _ string, _ io.Writer) error {
return cleanupErr
},
}
Expand Down
4 changes: 2 additions & 2 deletions scaletest/harness/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Runnable interface {
type Cleanable interface {
Runnable
// Cleanup should clean up any lingering resources from the test.
Cleanup(ctx context.Context, id string) error
Cleanup(ctx context.Context, id string, logs io.Writer) error
}

// AddRun creates a new *TestRun with the given name, ID and Runnable, adds it
Expand Down Expand Up @@ -131,7 +131,7 @@ func (r *TestRun) Cleanup(ctx context.Context) (err error) {
}
}()

err = c.Cleanup(ctx, r.id)
err = c.Cleanup(ctx, r.id, r.logs)
//nolint:revive // we use named returns because we mutate it in a defer
return
}
Expand Down
10 changes: 5 additions & 5 deletions scaletest/harness/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
type testFns struct {
RunFn func(ctx context.Context, id string, logs io.Writer) error
// CleanupFn is optional if no cleanup is required.
CleanupFn func(ctx context.Context, id string) error
CleanupFn func(ctx context.Context, id string, logs io.Writer) error
}

// Run implements Runnable.
Expand All @@ -25,12 +25,12 @@ func (fns testFns) Run(ctx context.Context, id string, logs io.Writer) error {
}

// Cleanup implements Cleanable.
func (fns testFns) Cleanup(ctx context.Context, id string) error {
func (fns testFns) Cleanup(ctx context.Context, id string, logs io.Writer) error {
if fns.CleanupFn == nil {
return nil
}

return fns.CleanupFn(ctx, id)
return fns.CleanupFn(ctx, id, logs)
}

func Test_TestRun(t *testing.T) {
Expand All @@ -49,7 +49,7 @@ func Test_TestRun(t *testing.T) {
atomic.AddInt64(&runCalled, 1)
return nil
},
CleanupFn: func(ctx context.Context, id string) error {
CleanupFn: func(ctx context.Context, id string, logs io.Writer) error {
atomic.AddInt64(&cleanupCalled, 1)
return nil
},
Expand Down Expand Up @@ -93,7 +93,7 @@ func Test_TestRun(t *testing.T) {
RunFn: func(ctx context.Context, id string, logs io.Writer) error {
return nil
},
CleanupFn: func(ctx context.Context, id string) error {
CleanupFn: func(ctx context.Context, id string, logs io.Writer) error {
atomic.AddInt64(&cleanupCalled, 1)
return nil
},
Expand Down
20 changes: 13 additions & 7 deletions scaletest/workspacebuild/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"net/http"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -106,25 +107,31 @@ func NewCleanupRunner(client *codersdk.Client, workspaceID uuid.UUID) *CleanupRu

// Run implements Runnable.
func (r *CleanupRunner) Run(ctx context.Context, _ string, logs io.Writer) error {
if r.workspaceID == uuid.Nil {
return nil
}
ctx, span := tracing.StartSpan(ctx)
defer span.End()

logs = loadtestutil.NewSyncWriter(logs)
logger := slog.Make(sloghuman.Sink(logs)).Leveled(slog.LevelDebug)
if r.workspaceID == uuid.Nil {
return nil
}
logger.Info(ctx, "deleting workspace", slog.F("workspace_id", r.workspaceID))
r.client.SetLogger(logger)
r.client.SetLogBodies(true)

ws, err := r.client.Workspace(ctx, r.workspaceID)
if err != nil {
var sdkErr *codersdk.Error
if xerrors.As(err, &sdkErr) && sdkErr.StatusCode() == http.StatusNotFound {
logger.Info(ctx, "workspace not found, skipping delete", slog.F("workspace_id", r.workspaceID))
return nil
}
return err
}

build, err := r.client.WorkspaceBuild(ctx, ws.LatestBuild.ID)
if err == nil && build.Job.Status.Active() {
// mark the build as canceled
logger.Info(ctx, "canceling workspace build", slog.F("build_id", build.ID), slog.F("workspace_id", r.workspaceID))
if err = r.client.CancelWorkspaceBuild(ctx, build.ID); err == nil {
// Wait for the job to cancel before we delete it
_ = waitForBuild(ctx, logs, r.client, build.ID) // it will return a "build canceled" error
Expand All @@ -151,12 +158,11 @@ func (r *CleanupRunner) Run(ctx context.Context, _ string, logs io.Writer) error
}

// Cleanup implements Cleanable by wrapping CleanupRunner.
func (r *Runner) Cleanup(ctx context.Context, id string) error {
// TODO: capture these logs
func (r *Runner) Cleanup(ctx context.Context, id string, w io.Writer) error {
return (&CleanupRunner{
client: r.client,
workspaceID: r.workspaceID,
}).Run(ctx, id, io.Discard)
}).Run(ctx, id, w)
}

func waitForBuild(ctx context.Context, w io.Writer, client *codersdk.Client, buildID uuid.UUID) error {
Expand Down
3 changes: 2 additions & 1 deletion scaletest/workspacebuild/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ func Test_Runner(t *testing.T) {
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaces[0].LatestBuild.ID)
coderdtest.AwaitWorkspaceAgents(t, client, workspaces[0].ID)

err = runner.Cleanup(ctx, "1")
cleanupLogs := bytes.NewBuffer(nil)
err = runner.Cleanup(ctx, "1", cleanupLogs)
require.NoError(t, err)
})

Expand Down
2 changes: 1 addition & 1 deletion scaletest/workspacetraffic/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (r *Runner) Run(ctx context.Context, _ string, logs io.Writer) error {
}

// Cleanup does nothing, successfully.
func (*Runner) Cleanup(context.Context, string) error {
func (*Runner) Cleanup(context.Context, string, io.Writer) error {
return nil
}

Expand Down