|
| 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 | +} |
0 commit comments