Skip to content

Commit 65335bc

Browse files
authored
feat: add cli command scaletest dynamic-parameters (coder#20034)
part of coder/internal#912 Adds CLI command `coder exp scaletest dynamic-parameters` I've left out the configuration of tracing and timeouts for now. I think I want to do some refactoring of the scaletest CLI to make handling those flags take up less boiler plate. I will add tracing and timeout flags in a follow up PR.
1 parent 0e0f092 commit 65335bc

File tree

8 files changed

+700
-7
lines changed

8 files changed

+700
-7
lines changed

cli/exp_scaletest.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func (r *RootCmd) scaletestCmd() *serpent.Command {
6060
Children: []*serpent.Command{
6161
r.scaletestCleanup(),
6262
r.scaletestDashboard(),
63+
r.scaletestDynamicParameters(),
6364
r.scaletestCreateWorkspaces(),
6465
r.scaletestWorkspaceUpdates(),
6566
r.scaletestWorkspaceTraffic(),
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//go:build !slim
2+
3+
package cli
4+
5+
import (
6+
"fmt"
7+
8+
"github.com/prometheus/client_golang/prometheus"
9+
"golang.org/x/xerrors"
10+
11+
"cdr.dev/slog"
12+
"cdr.dev/slog/sloggers/sloghuman"
13+
14+
"github.com/coder/coder/v2/scaletest/dynamicparameters"
15+
"github.com/coder/coder/v2/scaletest/harness"
16+
"github.com/coder/serpent"
17+
)
18+
19+
const (
20+
dynamicParametersTestName = "dynamic-parameters"
21+
)
22+
23+
func (r *RootCmd) scaletestDynamicParameters() *serpent.Command {
24+
var templateName string
25+
var numEvals int64
26+
orgContext := NewOrganizationContext()
27+
output := &scaletestOutputFlags{}
28+
29+
cmd := &serpent.Command{
30+
Use: "dynamic-parameters",
31+
Short: "Generates load on the Coder server evaluating dynamic parameters",
32+
Long: `It is recommended that all rate limits are disabled on the server before running this scaletest. This test generates many login events which will be rate limited against the (most likely single) IP.`,
33+
Handler: func(inv *serpent.Invocation) error {
34+
ctx := inv.Context()
35+
36+
outputs, err := output.parse()
37+
if err != nil {
38+
return xerrors.Errorf("could not parse --output flags")
39+
}
40+
41+
client, err := r.InitClient(inv)
42+
if err != nil {
43+
return err
44+
}
45+
if templateName == "" {
46+
return xerrors.Errorf("template cannot be empty")
47+
}
48+
49+
org, err := orgContext.Selected(inv, client)
50+
if err != nil {
51+
return err
52+
}
53+
54+
logger := slog.Make(sloghuman.Sink(inv.Stdout)).Leveled(slog.LevelDebug)
55+
partitions, err := dynamicparameters.SetupPartitions(ctx, client, org.ID, templateName, numEvals, logger)
56+
if err != nil {
57+
return xerrors.Errorf("setup dynamic parameters partitions: %w", err)
58+
}
59+
60+
th := harness.NewTestHarness(harness.ConcurrentExecutionStrategy{}, harness.ConcurrentExecutionStrategy{})
61+
reg := prometheus.NewRegistry()
62+
metrics := dynamicparameters.NewMetrics(reg, "concurrent_evaluations")
63+
64+
for i, part := range partitions {
65+
for j := range part.ConcurrentEvaluations {
66+
cfg := dynamicparameters.Config{
67+
TemplateVersion: part.TemplateVersion.ID,
68+
Metrics: metrics,
69+
MetricLabelValues: []string{fmt.Sprintf("%d", part.ConcurrentEvaluations)},
70+
}
71+
runner := dynamicparameters.NewRunner(client, cfg)
72+
th.AddRun(dynamicParametersTestName, fmt.Sprintf("%d/%d", j, i), runner)
73+
}
74+
}
75+
76+
err = th.Run(ctx)
77+
if err != nil {
78+
return xerrors.Errorf("run test harness: %w", err)
79+
}
80+
81+
res := th.Results()
82+
for _, o := range outputs {
83+
err = o.write(res, inv.Stdout)
84+
if err != nil {
85+
return xerrors.Errorf("write output %q to %q: %w", o.format, o.path, err)
86+
}
87+
}
88+
89+
return nil
90+
},
91+
}
92+
93+
cmd.Options = serpent.OptionSet{
94+
{
95+
Flag: "template",
96+
Description: "Name of the template to use. If it does not exist, it will be created.",
97+
Default: "scaletest-dynamic-parameters",
98+
Value: serpent.StringOf(&templateName),
99+
},
100+
{
101+
Flag: "concurrent-evaluations",
102+
Description: "Number of concurrent dynamic parameter evaluations to perform.",
103+
Default: "100",
104+
Value: serpent.Int64Of(&numEvals),
105+
},
106+
}
107+
orgContext.AttachOptions(cmd)
108+
output.attach(&cmd.Options)
109+
return cmd
110+
}

codersdk/client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,16 @@ func (e *Error) Error() string {
519519
return builder.String()
520520
}
521521

522+
// NewTestError is a helper function to create a Error, setting the internal fields. It's generally only useful for
523+
// testing.
524+
func NewTestError(statusCode int, method string, u string) *Error {
525+
return &Error{
526+
statusCode: statusCode,
527+
method: method,
528+
url: u,
529+
}
530+
}
531+
522532
type closeFunc func() error
523533

524534
func (c closeFunc) Close() error {

scaletest/dynamicparameters/config.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import "github.com/google/uuid"
44

55
type Config struct {
66
TemplateVersion uuid.UUID `json:"template_version"`
7-
SessionToken string `json:"session_token"`
87
Metrics *Metrics `json:"-"`
98
MetricLabelValues []string `json:"metric_label_values"`
109
}

scaletest/dynamicparameters/run.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,8 @@ type Runner struct {
2222
var _ harness.Runnable = &Runner{}
2323

2424
func NewRunner(client *codersdk.Client, cfg Config) *Runner {
25-
clone := codersdk.New(client.URL)
26-
clone.HTTPClient = client.HTTPClient
27-
clone.SetLogger(client.Logger())
28-
clone.SetSessionToken(cfg.SessionToken)
2925
return &Runner{
30-
client: clone,
26+
client: client,
3127
cfg: cfg,
3228
}
3329
}

scaletest/dynamicparameters/run_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ func TestRun(t *testing.T) {
3737
reg := prometheus.NewRegistry()
3838
cfg := dynamicparameters.Config{
3939
TemplateVersion: version.ID,
40-
SessionToken: userClient.SessionToken(),
4140
Metrics: dynamicparameters.NewMetrics(reg, "template", "test_label_name"),
4241
MetricLabelValues: []string{template.Name, "test_label_value"},
4342
}

0 commit comments

Comments
 (0)