mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add agentfake scaletest subcommand (#25072)
This PR builds on top of https://github.com/coder/coder/pull/25070 to add a way of running the larger "fake agent" manager via the existing CLI, pulling in the URL/credentials already set. With this, we can run a pod per scaletest region to act as all the workspaces in that region. This is in a new subcommand `scaletest agentfake` currently. --------- Signed-off-by: Callum Styan <callumstyan@gmail.com>
This commit is contained in:
@@ -472,7 +472,7 @@ func (f *workspaceTargetFlags) getTargetedWorkspaces(ctx context.Context, client
|
||||
return workspaces[targetStart:targetEnd], nil
|
||||
}
|
||||
|
||||
func requireAdmin(ctx context.Context, client *codersdk.Client) (codersdk.User, error) {
|
||||
func RequireAdmin(ctx context.Context, client *codersdk.Client) (codersdk.User, error) {
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
if err != nil {
|
||||
return codersdk.User{}, xerrors.Errorf("fetch current user: %w", err)
|
||||
@@ -617,7 +617,7 @@ func (r *RootCmd) scaletestCleanup() *serpent.Command {
|
||||
|
||||
ctx := inv.Context()
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -851,7 +851,7 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
|
||||
|
||||
ctx := inv.Context()
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1051,7 +1051,7 @@ func (r *RootCmd) scaletestCreateWorkspaces() *serpent.Command {
|
||||
{
|
||||
Flag: "no-wait-for-agents",
|
||||
Env: "CODER_SCALETEST_NO_WAIT_FOR_AGENTS",
|
||||
Description: `Do not wait for agents to start before marking the test as succeeded. This can be useful if you are running the test against a template that does not start the agent quickly.`,
|
||||
Description: `Do not wait for agents to start before marking the test as succeeded. This can be useful if you are running the test against a template that does not start the agent quickly. This is REQUIRED for templates whose workspaces use coder_external_agent resources, since external agents never connect on their own; pair with "coder exp scaletest agentfake" to drive those agents.`,
|
||||
Value: serpent.BoolOf(&noWaitForAgents),
|
||||
},
|
||||
{
|
||||
@@ -1177,7 +1177,7 @@ func (r *RootCmd) scaletestWorkspaceUpdates() *serpent.Command {
|
||||
defer stop()
|
||||
ctx = notifyCtx
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1473,7 +1473,7 @@ func (r *RootCmd) scaletestWorkspaceTraffic() *serpent.Command {
|
||||
defer stop()
|
||||
ctx = notifyCtx
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1925,7 +1925,7 @@ func (r *RootCmd) scaletestAutostart() *serpent.Command {
|
||||
defer stop()
|
||||
ctx = notifyCtx
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ Examples:
|
||||
|
||||
var userConfig createusers.Config
|
||||
if bridge.RequestMode(mode) == bridge.RequestModeBridge {
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (r *RootCmd) scaletestDynamicParameters() *serpent.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = requireAdmin(ctx, client)
|
||||
_, err = RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (r *RootCmd) scaletestNotifications() *serpent.Command {
|
||||
defer stop()
|
||||
ctx = notifyCtx
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func (r *RootCmd) scaletestPrebuilds() *serpent.Command {
|
||||
defer stop()
|
||||
ctx = notifyCtx
|
||||
|
||||
me, err := requireAdmin(ctx, client)
|
||||
me, err := RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ After all runners connect, it waits for the baseline duration before triggering
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = requireAdmin(ctx, client)
|
||||
_, err = RequireAdmin(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
//go:build !slim
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os/signal"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
agplcli "github.com/coder/coder/v2/cli"
|
||||
"github.com/coder/coder/v2/enterprise/scaletest/agentfake"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
// AGPLExperimental shadows the embedded RootCmd.AGPLExperimental to inject the
|
||||
// enterprise-only agentfake scaletest subcommand into the scaletest subtree.
|
||||
func (r *RootCmd) AGPLExperimental() []*serpent.Command {
|
||||
cmds := r.RootCmd.AGPLExperimental()
|
||||
for _, cmd := range cmds {
|
||||
if cmd.Use == "scaletest" {
|
||||
cmd.Children = append(cmd.Children, r.scaletestAgentFake())
|
||||
}
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
func (r *RootCmd) scaletestAgentFake() *serpent.Command {
|
||||
var (
|
||||
template string
|
||||
owner string
|
||||
)
|
||||
|
||||
cmd := &serpent.Command{
|
||||
Use: "agentfake",
|
||||
Short: "Run fake external agents against workspaces of the given template.",
|
||||
Long: agplcli.FormatExamples(
|
||||
agplcli.Example{
|
||||
Description: "Connect a fake agent for every external-agent workspace built from the template named " +
|
||||
"\"agentfake-runner\".",
|
||||
Command: "coder exp scaletest agentfake --template agentfake-runner",
|
||||
},
|
||||
) + "\n\n" +
|
||||
"Enumerates external-agent workspaces matching --template (optionally filtered by --owner), " +
|
||||
"fetches each workspace agent's external-agent credentials, and supervises one in-process fake " +
|
||||
"agent per token until the command is interrupted.\n\n" +
|
||||
"Requires a session token whose user is template-admin (or higher) on a deployment licensed " +
|
||||
"for the workspace external-agent feature; both the workspace builds and the credentials " +
|
||||
"endpoint are gated server-side. Pair with `coder exp scaletest create-workspaces " +
|
||||
"--no-wait-for-agents` to seed the workspaces this command will pick up. Workspaces created " +
|
||||
"after this command starts are NOT picked up; rerun the command after seeding more.",
|
||||
Handler: func(inv *serpent.Invocation) error {
|
||||
ctx := inv.Context()
|
||||
client, err := r.InitClient(inv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notifyCtx, stop := signal.NotifyContext(ctx, agplcli.StopSignals...)
|
||||
defer stop()
|
||||
ctx = notifyCtx
|
||||
|
||||
if _, err := agplcli.RequireAdmin(ctx, client); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if template == "" {
|
||||
return xerrors.New("--template is required")
|
||||
}
|
||||
|
||||
logger := inv.Logger
|
||||
mgr := agentfake.NewManager(client, logger, agentfake.ManagerOptions{
|
||||
Template: template,
|
||||
Owner: owner,
|
||||
})
|
||||
defer mgr.Close()
|
||||
|
||||
if err := mgr.Run(ctx); err != nil {
|
||||
return xerrors.Errorf("run agentfake manager: %w", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Options = serpent.OptionSet{
|
||||
{
|
||||
Flag: "template",
|
||||
Env: "CODER_SCALETEST_AGENTFAKE_TEMPLATE",
|
||||
Description: "Name of the template whose external-agent workspaces should be supervised. Required.",
|
||||
Value: serpent.StringOf(&template),
|
||||
},
|
||||
{
|
||||
Flag: "owner",
|
||||
Env: "CODER_SCALETEST_AGENTFAKE_OWNER",
|
||||
Description: "Optional workspace-owner filter (username). When empty, all owners' workspaces of the template are included.",
|
||||
Value: serpent.StringOf(&owner),
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
Reference in New Issue
Block a user