Files
coder/scaletest/dashboard/run.go
Kyle Carberry 22e781eced chore: add /v2 to import module path (#9072)
* chore: add /v2 to import module path

go mod requires semantic versioning with versions greater than 1.x

This was a mechanical update by running:
```
go install github.com/marwan-at-work/mod/cmd/mod@latest
mod upgrade
```

Migrate generated files to import /v2

* Fix gen
2023-08-18 18:55:43 +00:00

132 lines
2.6 KiB
Go

package dashboard
import (
"context"
"fmt"
"io"
"math/rand"
"time"
"golang.org/x/xerrors"
"cdr.dev/slog"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/scaletest/harness"
)
type Runner struct {
client *codersdk.Client
cfg Config
metrics Metrics
}
var (
_ harness.Runnable = &Runner{}
_ harness.Cleanable = &Runner{}
)
func NewRunner(client *codersdk.Client, metrics Metrics, cfg Config) *Runner {
client.Trace = cfg.Trace
return &Runner{
client: client,
cfg: cfg,
metrics: metrics,
}
}
func (r *Runner) Run(ctx context.Context, _ string, _ io.Writer) error {
me, err := r.client.User(ctx, codersdk.Me)
if err != nil {
return err
}
if len(me.OrganizationIDs) == 0 {
return xerrors.Errorf("user has no organizations")
}
c := &cache{}
if err := c.fill(ctx, r.client); err != nil {
return err
}
p := &Params{
client: r.client,
me: me,
c: c,
}
rolls := make(chan int)
go func() {
t := time.NewTicker(r.randWait())
defer t.Stop()
for {
select {
case <-ctx.Done():
return
case <-t.C:
rolls <- rand.Intn(r.cfg.RollTable.max() + 1) // nolint:gosec
t.Reset(r.randWait())
}
}
}()
for {
select {
case <-ctx.Done():
return nil
case n := <-rolls:
act := r.cfg.RollTable.choose(n)
go r.do(ctx, act, p)
}
}
}
func (*Runner) Cleanup(_ context.Context, _ string) error {
return nil
}
func (r *Runner) do(ctx context.Context, act RollTableEntry, p *Params) {
select {
case <-ctx.Done():
r.cfg.Logger.Info(ctx, "context done, stopping")
return
default:
var errored bool
cancelCtx, cancel := context.WithTimeout(ctx, r.cfg.MaxWait)
defer cancel()
start := time.Now()
err := act.Fn(cancelCtx, p)
cancel()
elapsed := time.Since(start)
if err != nil {
errored = true
r.cfg.Logger.Error( //nolint:gocritic
ctx, "action failed",
slog.Error(err),
slog.F("action", act.Label),
slog.F("elapsed", elapsed),
)
} else {
r.cfg.Logger.Info(ctx, "completed successfully",
slog.F("action", act.Label),
slog.F("elapsed", elapsed),
)
}
codeLabel := "200"
if apiErr, ok := codersdk.AsError(err); ok {
codeLabel = fmt.Sprintf("%d", apiErr.StatusCode())
} else if xerrors.Is(err, context.Canceled) {
codeLabel = "timeout"
}
r.metrics.ObserveDuration(act.Label, elapsed)
r.metrics.IncStatuses(act.Label, codeLabel)
if errored {
r.metrics.IncErrors(act.Label)
}
}
}
func (r *Runner) randWait() time.Duration {
// nolint:gosec // This is not for cryptographic purposes. Chill, gosec. Chill.
wait := time.Duration(rand.Intn(int(r.cfg.MaxWait) - int(r.cfg.MinWait)))
return r.cfg.MinWait + wait
}