Files
coder/scaletest/createworkspaces/run.go
T
Spike Curtis bddb808b25 chore: arrange imports in a standard way (#21452)
Fixes all our Go file imports to match the preferred spec that we've _mostly_ been using. For example:

```
import (
	"context"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/xerrors"
	"gopkg.in/natefinch/lumberjack.v2"

	"cdr.dev/slog/v3"
	"github.com/coder/coder/v2/codersdk/agentsdk"
	"github.com/coder/serpent"
)
```

3 groups: standard library, 3rd partly libs, Coder libs.

This PR makes the change across the codebase. The PR in the stack above modifies our formatting to maintain this state of affairs, and is a separate PR so it's possible to review that one in detail.
2026-01-08 15:24:11 +04:00

176 lines
4.5 KiB
Go

package createworkspaces
import (
"context"
"fmt"
"io"
"github.com/google/uuid"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"
"cdr.dev/slog/v3"
"cdr.dev/slog/v3/sloggers/sloghuman"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/scaletest/agentconn"
"github.com/coder/coder/v2/scaletest/createusers"
"github.com/coder/coder/v2/scaletest/harness"
"github.com/coder/coder/v2/scaletest/loadtestutil"
"github.com/coder/coder/v2/scaletest/reconnectingpty"
"github.com/coder/coder/v2/scaletest/workspacebuild"
)
type Runner struct {
client *codersdk.Client
cfg Config
createUserRunner *createusers.Runner
workspacebuildRunner *workspacebuild.Runner
}
var (
_ harness.Runnable = &Runner{}
_ harness.Cleanable = &Runner{}
)
func NewRunner(client *codersdk.Client, cfg Config) *Runner {
return &Runner{
client: client,
cfg: cfg,
}
}
// Run implements Runnable.
func (r *Runner) Run(ctx context.Context, id string, logs io.Writer) error {
ctx, span := tracing.StartSpan(ctx)
defer span.End()
logs = loadtestutil.NewSyncWriter(logs)
logger := slog.Make(sloghuman.Sink(logs)).Leveled(slog.LevelDebug)
r.client.SetLogger(logger)
r.client.SetLogBodies(true)
var (
client = r.client
user codersdk.User
err error
)
if r.cfg.User.SessionToken != "" {
_, _ = fmt.Fprintln(logs, "Using existing user session token:")
user, err = client.User(ctx, "me")
if err != nil {
return xerrors.Errorf("generate random password for user: %w", err)
}
} else {
createUserConfig := createusers.Config{
OrganizationID: r.cfg.User.OrganizationID,
Username: r.cfg.User.Username,
Email: r.cfg.User.Email,
}
if err := createUserConfig.Validate(); err != nil {
return xerrors.Errorf("validate create user config: %w", err)
}
r.createUserRunner = createusers.NewRunner(r.client, createUserConfig)
newUser, err := r.createUserRunner.RunReturningUser(ctx, id, logs)
if err != nil {
return xerrors.Errorf("create user: %w", err)
}
user = newUser.User
client = codersdk.New(r.client.URL)
client.SetSessionToken(newUser.SessionToken)
}
_, _ = fmt.Fprintln(logs, "\nCreating workspace...")
workspaceBuildConfig := r.cfg.Workspace
workspaceBuildConfig.OrganizationID = r.cfg.User.OrganizationID
workspaceBuildConfig.UserID = user.ID.String()
r.workspacebuildRunner = workspacebuild.NewRunner(client, workspaceBuildConfig)
slimWorkspace, err := r.workspacebuildRunner.RunReturningWorkspace(ctx, id, logs)
if err != nil {
return xerrors.Errorf("create workspace: %w", err)
}
workspace, err := client.Workspace(ctx, slimWorkspace.ID)
if err != nil {
return xerrors.Errorf("get full workspace info: %w", err)
}
if r.cfg.Workspace.NoWaitForAgents {
return nil
}
// Find the first agent.
var agent codersdk.WorkspaceAgent
resourceLoop:
for _, res := range workspace.LatestBuild.Resources {
for _, a := range res.Agents {
agent = a
break resourceLoop
}
}
if agent.ID == uuid.Nil {
return xerrors.Errorf("no agents found for workspace %q", workspace.ID.String())
}
eg, egCtx := errgroup.WithContext(ctx)
if r.cfg.ReconnectingPTY != nil {
eg.Go(func() error {
reconnectingPTYConfig := *r.cfg.ReconnectingPTY
reconnectingPTYConfig.AgentID = agent.ID
reconnectingPTYRunner := reconnectingpty.NewRunner(client, reconnectingPTYConfig)
err := reconnectingPTYRunner.Run(egCtx, id, logs)
if err != nil {
return xerrors.Errorf("run reconnecting pty: %w", err)
}
return nil
})
}
if r.cfg.AgentConn != nil {
eg.Go(func() error {
agentConnConfig := *r.cfg.AgentConn
agentConnConfig.AgentID = agent.ID
agentConnRunner := agentconn.NewRunner(client, agentConnConfig)
err := agentConnRunner.Run(egCtx, id, logs)
if err != nil {
return xerrors.Errorf("run agent connection: %w", err)
}
return nil
})
}
err = eg.Wait()
if err != nil {
return xerrors.Errorf("run workspace connections in parallel: %w", err)
}
return nil
}
// Cleanup implements Cleanable.
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, logs)
if err != nil {
return xerrors.Errorf("cleanup workspace: %w", err)
}
}
if r.createUserRunner != nil {
err := r.createUserRunner.Cleanup(ctx, id, logs)
if err != nil {
return xerrors.Errorf("cleanup user: %w", err)
}
}
return nil
}