Skip to content

Commit

Permalink
[1/2] Implement the --local-execution mode for k6 cloud run (#3904)
Browse files Browse the repository at this point in the history
* Implement the --local-execution mode for k6 cloud run

* Explicit the handling of the --no-usage-report option

This commits aligns the handling of the --no-usage-report option in the
context of the k6 cloud run command with the following agreed upon
behavior:
* k6 cloud run --no-usage-report should ignore the flag, and make
sure we don't send the report.
* k6 cloud run --local-execution --no-usage-report  should take the
flag in consideration, and act accordingly.

* Fix tests

* Update cmd/cloud_run.go

Co-authored-by: Ivan <2103732+codebien@users.noreply.github.com>

* Apply pull request review suggestions

* Apply pull request review suggestions

* Update cmd/cloud_run.go

Co-authored-by: Oleg Bespalov <oleg.bespalov@grafana.com>

* Fix linting errors

* Apply Pull Request suggestions

---------

Co-authored-by: Ivan <2103732+codebien@users.noreply.github.com>
Co-authored-by: Oleg Bespalov <oleg.bespalov@grafana.com>
  • Loading branch information
3 people committed Sep 10, 2024
1 parent 93667ec commit 999a614
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 17 deletions.
2 changes: 1 addition & 1 deletion cmd/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ service. Be sure to run the "k6 cloud login" command prior to authenticate with
}

// Register `k6 cloud` subcommands
cloudCmd.AddCommand(getCmdCloudRun(gs))
cloudCmd.AddCommand(getCmdCloudRun(c))
cloudCmd.AddCommand(getCmdCloudLogin(gs))
cloudCmd.AddCommand(getCmdCloudUpload(c))

Expand Down
154 changes: 140 additions & 14 deletions cmd/cloud_run.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,66 @@
package cmd

import (
"fmt"

"go.k6.io/k6/errext/exitcodes"

"go.k6.io/k6/errext"

"github.com/spf13/cobra"
"go.k6.io/k6/cmd/state"
"github.com/spf13/pflag"
"go.k6.io/k6/execution"
"go.k6.io/k6/execution/local"
)

const cloudRunCommandName string = "run"

func getCmdCloudRun(gs *state.GlobalState) *cobra.Command {
deprecatedCloudCmd := &cmdCloud{
gs: gs,
showCloudLogs: true,
exitOnRunning: false,
uploadOnly: false,
type cmdCloudRun struct {
// localExecution stores the state of the --local-execution flag.
localExecution bool

// linger stores the state of the --linger flag.
linger bool

// noUsageReport stores the state of the --no-usage-report flag.
noUsageReport bool

// runCmd holds an instance of the k6 run command that we store
// in order to be able to call its run method to support
// the --local-execution flag mode.
runCmd *cmdRun

// deprecatedCloudCmd holds an instance of the k6 cloud command that we store
// in order to be able to call its run method to support the cloud execution
// feature, and to have access to its flagSet if necessary.
deprecatedCloudCmd *cmdCloud
}

func getCmdCloudRun(cloudCmd *cmdCloud) *cobra.Command {
// We instantiate the run command here to be able to call its run method
// when the --local-execution flag is set.
runCmd := &cmdRun{
gs: cloudCmd.gs,

// We override the loadConfiguredTest func to use the local execution
// configuration which enforces the use of the cloud output among other
// side effects.
loadConfiguredTest: func(cmd *cobra.Command, args []string) (
*loadedAndConfiguredTest,
execution.Controller,
error,
) {
test, err := loadAndConfigureLocalTest(cloudCmd.gs, cmd, args, getCloudRunLocalExecutionConfig)
return test, local.NewController(), err
},
}

cloudRunCmd := &cmdCloudRun{
deprecatedCloudCmd: cloudCmd,
runCmd: runCmd,
}

exampleText := getExampleText(gs, `
exampleText := getExampleText(cloudCmd.gs, `
# Run a test script in Grafana Cloud k6
$ {{.}} cloud run script.js
Expand All @@ -25,7 +70,7 @@ func getCmdCloudRun(gs *state.GlobalState) *cobra.Command {
# Read a test script or archive from stdin and run it in Grafana Cloud k6
$ {{.}} cloud run - < script.js`[1:])

cloudRunCmd := &cobra.Command{
thisCmd := &cobra.Command{
Use: cloudRunCommandName,
Short: "Run a test in Grafana Cloud k6",
Long: `Run a test in Grafana Cloud k6.
Expand All @@ -38,12 +83,93 @@ Use the "k6 cloud login" command to authenticate.`,
"the k6 cloud run command expects a single argument consisting in either a path to a script or "+
"archive file, or the \"-\" symbol indicating the script or archive should be read from stdin",
),
PreRunE: deprecatedCloudCmd.preRun,
RunE: deprecatedCloudCmd.run,
PreRunE: cloudRunCmd.preRun,
RunE: cloudRunCmd.run,
}

thisCmd.Flags().SortFlags = false
thisCmd.Flags().AddFlagSet(cloudRunCmd.flagSet())
thisCmd.Flags().AddFlagSet(cloudCmd.flagSet())

return thisCmd
}

func (c *cmdCloudRun) preRun(cmd *cobra.Command, args []string) error {
if c.localExecution {
if cmd.Flags().Changed("exit-on-running") {
return errext.WithExitCodeIfNone(
fmt.Errorf("the --local-execution flag is not compatible with the --exit-on-running flag"),
exitcodes.InvalidConfig,
)
}

if cmd.Flags().Changed("show-logs") {
return errext.WithExitCodeIfNone(
fmt.Errorf("the --local-execution flag is not compatible with the --show-logs flag"),
exitcodes.InvalidConfig,
)
}

return nil
}

if c.linger {
return errext.WithExitCodeIfNone(
fmt.Errorf("the --linger flag can only be used in conjunction with the --local-execution flag"),
exitcodes.InvalidConfig,
)
}

return c.deprecatedCloudCmd.preRun(cmd, args)
}

func (c *cmdCloudRun) run(cmd *cobra.Command, args []string) error {
if c.localExecution {
return c.runCmd.run(cmd, args)
}

// When running the `k6 cloud run` command explicitly disable the usage report.
c.noUsageReport = true

return c.deprecatedCloudCmd.run(cmd, args)
}

func (c *cmdCloudRun) flagSet() *pflag.FlagSet {
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
flags.SortFlags = false

flags.BoolVar(&c.localExecution, "local-execution", c.localExecution,
"executes the test locally instead of in the cloud")
flags.BoolVar(
&c.linger,
"linger",
c.linger,
"only when using the local-execution mode, keeps the API server alive past the test end",
)
flags.BoolVar(
&c.noUsageReport,
"no-usage-report",
c.noUsageReport,
"only when using the local-execution mode, don't send anonymous usage "+
"stats (https://grafana.com/docs/k6/latest/set-up/usage-collection/)",
)

return flags
}

func getCloudRunLocalExecutionConfig(flags *pflag.FlagSet) (Config, error) {
opts, err := getOptions(flags)
if err != nil {
return Config{}, err
}

cloudRunCmd.Flags().SortFlags = false
cloudRunCmd.Flags().AddFlagSet(deprecatedCloudCmd.flagSet())
// When running locally, we force the output to be cloud.
out := []string{"cloud"}

return cloudRunCmd
return Config{
Options: opts,
Out: out,
Linger: getNullBool(flags, "linger"),
NoUsageReport: getNullBool(flags, "no-usage-report"),
}, nil
}
6 changes: 5 additions & 1 deletion cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ func configFlagSet() *pflag.FlagSet {
flags.SortFlags = false
flags.StringArrayP("out", "o", []string{}, "`uri` for an external metrics database")
flags.BoolP("linger", "l", false, "keep the API server alive past test end")
flags.Bool("no-usage-report", false, "don't send anonymous stats to the developers")
flags.Bool(
"no-usage-report",
false,
"don't send anonymous usage"+"stats (https://grafana.com/docs/k6/latest/set-up/usage-collection/)",
)
return flags
}

Expand Down
50 changes: 49 additions & 1 deletion cmd/tests/cmd_cloud_run_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package tests

import "testing"
import (
"testing"

"go.k6.io/k6/errext/exitcodes"

"github.com/stretchr/testify/assert"
"go.k6.io/k6/cmd"
)

func TestK6CloudRun(t *testing.T) {
t.Parallel()
Expand All @@ -10,3 +17,44 @@ func TestK6CloudRun(t *testing.T) {
func setupK6CloudRunCmd(cliFlags []string) []string {
return append([]string{"k6", "cloud", "run"}, append(cliFlags, "test.js")...)
}

func TestCloudRunCommandIncompatibleFlags(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
cliArgs []string
wantStderrContains string
}{
{
name: "using --linger should be incompatible with k6 cloud run",
cliArgs: []string{"--linger"},
wantStderrContains: "the --linger flag can only be used in conjunction with the --local-execution flag",
},
{
name: "using --exit-on-running should be incompatible with k6 cloud run --local-execution",
cliArgs: []string{"--local-execution", "--exit-on-running"},
wantStderrContains: "the --local-execution flag is not compatible with the --exit-on-running flag",
},
{
name: "using --show-logs should be incompatible with k6 cloud run --local-execution",
cliArgs: []string{"--local-execution", "--show-logs"},
wantStderrContains: "the --local-execution flag is not compatible with the --show-logs flag",
},
}

for _, tc := range testCases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

ts := getSimpleCloudTestState(t, nil, setupK6CloudRunCmd, tc.cliArgs, nil, nil)
ts.ExpectedExitCode = int(exitcodes.InvalidConfig)
cmd.ExecuteWithGlobalState(ts.GlobalState)

stderr := ts.Stderr.String()
assert.Contains(t, stderr, tc.wantStderrContains)
})
}
}

0 comments on commit 999a614

Please sign in to comment.