From 0e90b2ccf32913f4b67df928835c90864219c6da Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 19 Jan 2024 16:00:32 +0200 Subject: [PATCH 1/4] feat(cli/exp): add --target-workspaces to scaletest workspace-traffic command --- cli/exp_scaletest.go | 59 +++++++++++++++++++++++++++++++++++---- cli/exp_scaletest_test.go | 25 +++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/cli/exp_scaletest.go b/cli/exp_scaletest.go index b1bafbdbb6c77..8efb0758532f4 100644 --- a/cli/exp_scaletest.go +++ b/cli/exp_scaletest.go @@ -857,11 +857,12 @@ func (r *RootCmd) scaletestCreateWorkspaces() *clibase.Cmd { func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { var ( - tickInterval time.Duration - bytesPerTick int64 - ssh bool - app string - template string + tickInterval time.Duration + bytesPerTick int64 + ssh bool + app string + template string + targetWorkspaces string client = &codersdk.Client{} tracingFlags = &scaletestTracingFlags{} @@ -912,6 +913,10 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { return xerrors.Errorf("parse template: %w", err) } } + targetWorkspaceStart, targetWorkspaceEnd, err := parseTargetWorkspaces(targetWorkspaces) + if err != nil { + return xerrors.Errorf("parse target workspaces: %w", err) + } appHost, err := client.AppHost(ctx) if err != nil { @@ -923,9 +928,16 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { return err } + if targetWorkspaceEnd == 0 { + targetWorkspaceEnd = len(workspaces) + } + if len(workspaces) == 0 { return xerrors.Errorf("no scaletest workspaces exist") } + if targetWorkspaceEnd > len(workspaces) { + return xerrors.Errorf("target workspace end %d is greater than the number of workspaces %d", targetWorkspaceEnd, len(workspaces)) + } tracerProvider, closeTracing, tracingEnabled, err := tracingFlags.provider(ctx) if err != nil { @@ -951,6 +963,10 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { th := harness.NewTestHarness(strategy.toStrategy(), cleanupStrategy.toStrategy()) for idx, ws := range workspaces { + if idx < targetWorkspaceStart || idx >= targetWorkspaceEnd { + continue + } + var ( agent codersdk.WorkspaceAgent name = "workspace-traffic" @@ -1039,6 +1055,12 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { Description: "Name or ID of the template. Traffic generation will be limited to workspaces created from this template.", Value: clibase.StringOf(&template), }, + { + Flag: "target-workspaces", + Env: "CODER_SCALETEST_TARGET_WORKSPACES", + Description: "Target a specific range of workspaces in the format [START]:[END] (exclusive). Example: 0:10 will target workspaces the 10 first alphabetically sorted workspaces (0-9).", + Value: clibase.StringOf(&targetWorkspaces), + }, { Flag: "bytes-per-tick", Env: "CODER_SCALETEST_WORKSPACE_TRAFFIC_BYTES_PER_TICK", @@ -1430,6 +1452,33 @@ func parseTemplate(ctx context.Context, client *codersdk.Client, organizationIDs return tpl, nil } +func parseTargetWorkspaces(targetWorkspaces string) (start, end int, err error) { + if targetWorkspaces == "" { + return 0, 0, nil + } + + parts := strings.Split(targetWorkspaces, ":") + if len(parts) != 2 { + return 0, 0, xerrors.Errorf("invalid target workspaces %q", targetWorkspaces) + } + + start, err = strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, xerrors.Errorf("invalid target workspaces %q: %w", targetWorkspaces, err) + } + + end, err = strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, xerrors.Errorf("invalid target workspaces %q: %w", targetWorkspaces, err) + } + + if start == end { + return 0, 0, xerrors.Errorf("invalid target workspaces %q: start and end cannot be equal", targetWorkspaces) + } + + return start, end, nil +} + func createWorkspaceAppConfig(client *codersdk.Client, appHost, app string, workspace codersdk.Workspace, agent codersdk.WorkspaceAgent) (workspacetraffic.AppConfig, error) { if app == "" { return workspacetraffic.AppConfig{}, nil diff --git a/cli/exp_scaletest_test.go b/cli/exp_scaletest_test.go index a96d0daaa9014..ac35e8d3e84d6 100644 --- a/cli/exp_scaletest_test.go +++ b/cli/exp_scaletest_test.go @@ -116,6 +116,31 @@ func TestScaleTestWorkspaceTraffic_Template(t *testing.T) { require.ErrorContains(t, err, "could not find template \"doesnotexist\" in any organization") } +// This test just validates that the CLI command accepts its known arguments. +func TestScaleTestWorkspaceTraffic_TargetWorkspaces(t *testing.T) { + t.Parallel() + + ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancelFunc() + + log := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}) + client := coderdtest.New(t, &coderdtest.Options{ + Logger: &log, + }) + _ = coderdtest.CreateFirstUser(t, client) + + inv, root := clitest.New(t, "exp", "scaletest", "workspace-traffic", + "--target-workspaces", "0:0", + ) + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t) + inv.Stdout = pty.Output() + inv.Stderr = pty.Output() + + err := inv.WithContext(ctx).Run() + require.ErrorContains(t, err, "invalid target workspaces \"0:0\": start and end cannot be equal") +} + // This test just validates that the CLI command accepts its known arguments. func TestScaleTestCleanup_Template(t *testing.T) { t.Parallel() From 5ee951819775b1675db59abe2038f57b9827b92d Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 19 Jan 2024 16:06:20 +0200 Subject: [PATCH 2/4] fix desc --- cli/exp_scaletest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/exp_scaletest.go b/cli/exp_scaletest.go index 8efb0758532f4..a550b12a653ea 100644 --- a/cli/exp_scaletest.go +++ b/cli/exp_scaletest.go @@ -1058,7 +1058,7 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { { Flag: "target-workspaces", Env: "CODER_SCALETEST_TARGET_WORKSPACES", - Description: "Target a specific range of workspaces in the format [START]:[END] (exclusive). Example: 0:10 will target workspaces the 10 first alphabetically sorted workspaces (0-9).", + Description: "Target a specific range of workspaces in the format [START]:[END] (exclusive). Example: 0:10 will target the 10 first alphabetically sorted workspaces (0-9).", Value: clibase.StringOf(&targetWorkspaces), }, { From fc5869ac75f365bb27ea1ce8b0cded981ad455b9 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 19 Jan 2024 16:26:22 +0200 Subject: [PATCH 3/4] extend to dashboard traffic (users) --- cli/exp_scaletest.go | 44 +++++++++++++++++++++++++++------------ cli/exp_scaletest_test.go | 23 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/cli/exp_scaletest.go b/cli/exp_scaletest.go index a550b12a653ea..46d486521e17b 100644 --- a/cli/exp_scaletest.go +++ b/cli/exp_scaletest.go @@ -913,7 +913,7 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { return xerrors.Errorf("parse template: %w", err) } } - targetWorkspaceStart, targetWorkspaceEnd, err := parseTargetWorkspaces(targetWorkspaces) + targetWorkspaceStart, targetWorkspaceEnd, err := parseTargetRange("workspaces", targetWorkspaces) if err != nil { return xerrors.Errorf("parse target workspaces: %w", err) } @@ -1102,10 +1102,11 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *clibase.Cmd { func (r *RootCmd) scaletestDashboard() *clibase.Cmd { var ( - interval time.Duration - jitter time.Duration - headless bool - randSeed int64 + interval time.Duration + jitter time.Duration + headless bool + randSeed int64 + targetUsers string client = &codersdk.Client{} tracingFlags = &scaletestTracingFlags{} @@ -1128,6 +1129,10 @@ func (r *RootCmd) scaletestDashboard() *clibase.Cmd { if !(jitter < interval) { return xerrors.Errorf("--jitter must be less than --interval") } + targetUserStart, targetUserEnd, err := parseTargetRange("users", targetUsers) + if err != nil { + return xerrors.Errorf("parse target users: %w", err) + } ctx := inv.Context() logger := inv.Logger.AppendSinks(sloghuman.Sink(inv.Stdout)) if r.verbose { @@ -1164,8 +1169,15 @@ func (r *RootCmd) scaletestDashboard() *clibase.Cmd { if err != nil { return xerrors.Errorf("get scaletest users") } + if targetUserEnd == 0 { + targetUserEnd = len(users) + } + + for idx, usr := range users { + if idx < targetUserStart || idx >= targetUserEnd { + continue + } - for _, usr := range users { //nolint:gosec // not used for cryptographic purposes rndGen := rand.New(rand.NewSource(randSeed)) name := fmt.Sprintf("dashboard-%s", usr.Username) @@ -1236,6 +1248,12 @@ func (r *RootCmd) scaletestDashboard() *clibase.Cmd { } cmd.Options = []clibase.Option{ + { + Flag: "target-users", + Env: "CODER_SCALETEST_DASHBOARD_TARGET_USERS", + Description: "Target a specific range of users in the format [START]:[END] (exclusive). Example: 0:10 will target the 10 first alphabetically sorted users (0-9).", + Value: clibase.StringOf(&targetUsers), + }, { Flag: "interval", Env: "CODER_SCALETEST_DASHBOARD_INTERVAL", @@ -1452,28 +1470,28 @@ func parseTemplate(ctx context.Context, client *codersdk.Client, organizationIDs return tpl, nil } -func parseTargetWorkspaces(targetWorkspaces string) (start, end int, err error) { - if targetWorkspaces == "" { +func parseTargetRange(name, targets string) (start, end int, err error) { + if targets == "" { return 0, 0, nil } - parts := strings.Split(targetWorkspaces, ":") + parts := strings.Split(targets, ":") if len(parts) != 2 { - return 0, 0, xerrors.Errorf("invalid target workspaces %q", targetWorkspaces) + return 0, 0, xerrors.Errorf("invalid target %s %q", name, targets) } start, err = strconv.Atoi(parts[0]) if err != nil { - return 0, 0, xerrors.Errorf("invalid target workspaces %q: %w", targetWorkspaces, err) + return 0, 0, xerrors.Errorf("invalid target %s %q: %w", name, targets, err) } end, err = strconv.Atoi(parts[1]) if err != nil { - return 0, 0, xerrors.Errorf("invalid target workspaces %q: %w", targetWorkspaces, err) + return 0, 0, xerrors.Errorf("invalid target %s %q: %w", name, targets, err) } if start == end { - return 0, 0, xerrors.Errorf("invalid target workspaces %q: start and end cannot be equal", targetWorkspaces) + return 0, 0, xerrors.Errorf("invalid target %s %q: start and end cannot be equal", name, targets) } return start, end, nil diff --git a/cli/exp_scaletest_test.go b/cli/exp_scaletest_test.go index ac35e8d3e84d6..27f1adaac6c7d 100644 --- a/cli/exp_scaletest_test.go +++ b/cli/exp_scaletest_test.go @@ -243,4 +243,27 @@ func TestScaleTestDashboard(t *testing.T) { err := inv.WithContext(ctx).Run() require.NoError(t, err, "") }) + + t.Run("TargetUsers", func(t *testing.T) { + t.Parallel() + ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancelFunc() + + log := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}) + client := coderdtest.New(t, &coderdtest.Options{ + Logger: &log, + }) + _ = coderdtest.CreateFirstUser(t, client) + + inv, root := clitest.New(t, "exp", "scaletest", "dashboard", + "--target-users", "0:0", + ) + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t) + inv.Stdout = pty.Output() + inv.Stderr = pty.Output() + + err := inv.WithContext(ctx).Run() + require.ErrorContains(t, err, "invalid target users \"0:0\": start and end cannot be equal") + }) } From 3e0aeefcb6c89a6a746880116f2706fdb55ad5e3 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 19 Jan 2024 17:25:36 +0200 Subject: [PATCH 4/4] add extra check --- cli/exp_scaletest.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/exp_scaletest.go b/cli/exp_scaletest.go index 46d486521e17b..64cdd1f0a5b92 100644 --- a/cli/exp_scaletest.go +++ b/cli/exp_scaletest.go @@ -1493,6 +1493,9 @@ func parseTargetRange(name, targets string) (start, end int, err error) { if start == end { return 0, 0, xerrors.Errorf("invalid target %s %q: start and end cannot be equal", name, targets) } + if end < start { + return 0, 0, xerrors.Errorf("invalid target %s %q: end cannot be less than start", name, targets) + } return start, end, nil }