-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move cloud run and login functionalities under cloud subcommands (#3813)
* Introduce a `k6 cloud run` command Important notice: this commit declare a cobra sub-command holding the logic for the `k6 cloud run` sub-command, but does not register it. In this commit, we duplicate the logic from the existing `k6 cloud` logic, with very little adjustments, to support the later registry of the `k6 cloud run` command. To simplify the collaboration on this and further reviews, we delegate any refactoring of the original cloud command's logic, to a further commit or Pull Request. * Introduce a `k6 cloud login` command Important notice: this commit declare a cobra sub-command holding the logic for the `k6 cloud login` sub-command, but does not register it. In this commit, we duplicate the logic from the existing `k6 login` logic, with very little adjustments, to support the later registry of the `k6 cloud login` command. To simplify the collaboration on this and further reviews, we delegate any refactoring of the original cloud command's logic, to a further commit or Pull Request. This new `k6 cloud login` command is notably focusing solely on authenticating with the Grafana Cloud k6, and by design does not aim to support InfluxDB authentication. * Register run and login subcommands of the cloud command * Add deprecation warning to k6 login and k6 login cloud commands * FIXME add tests assert k6 cloud run command's arguments handling * Apply Pull-Request suggestions Co-authored-by: Joan López de la Franca Beltran <[email protected]> * Improve cloud commands missing arguments handling * Apply suggestions from code review * Refactor cloud run command and tests to leverage existing code * Apply suggestions from code review Co-authored-by: Oleg Bespalov <[email protected]> * fix lint of cmd package --------- Co-authored-by: Joan López de la Franca Beltran <[email protected]> Co-authored-by: Oleg Bespalov <[email protected]>
- Loading branch information
1 parent
d805563
commit aab3bc8
Showing
7 changed files
with
526 additions
and
217 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"syscall" | ||
|
||
"github.com/fatih/color" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/term" | ||
"gopkg.in/guregu/null.v3" | ||
|
||
"go.k6.io/k6/cloudapi" | ||
"go.k6.io/k6/cmd/state" | ||
"go.k6.io/k6/lib/consts" | ||
"go.k6.io/k6/ui" | ||
) | ||
|
||
const cloudLoginCommandName = "login" | ||
|
||
type cmdCloudLogin struct { | ||
globalState *state.GlobalState | ||
} | ||
|
||
func getCmdCloudLogin(gs *state.GlobalState) *cobra.Command { | ||
c := &cmdCloudLogin{ | ||
globalState: gs, | ||
} | ||
|
||
// loginCloudCommand represents the 'cloud login' command | ||
exampleText := getExampleText(gs, ` | ||
# Log in with an email/password | ||
{{.}} cloud login | ||
# Store a token in k6's persistent configuration | ||
{{.}} cloud login -t <YOUR_TOKEN> | ||
# Display the stored token | ||
{{.}} cloud login -s | ||
# Reset the stored token | ||
{{.}} cloud login -r`[1:]) | ||
|
||
loginCloudCommand := &cobra.Command{ | ||
Use: cloudLoginCommandName, | ||
Short: "Authenticate with Grafana Cloud k6", | ||
Long: `Authenticate with Grafana Cloud k6. | ||
This command will authenticate you with Grafana Cloud k6. | ||
Once authenticated you can start running tests in the cloud by using the "k6 cloud run" | ||
command, or by executing a test locally and outputting samples to the cloud using | ||
the "k6 run -o cloud" command. | ||
`, | ||
Example: exampleText, | ||
Args: cobra.NoArgs, | ||
RunE: c.run, | ||
} | ||
|
||
loginCloudCommand.Flags().StringP("token", "t", "", "specify `token` to use") | ||
loginCloudCommand.Flags().BoolP("show", "s", false, "display saved token and exit") | ||
loginCloudCommand.Flags().BoolP("reset", "r", false, "reset stored token") | ||
|
||
return loginCloudCommand | ||
} | ||
|
||
// run is the code that runs when the user executes `k6 cloud login` | ||
// | ||
//nolint:funlen | ||
func (c *cmdCloudLogin) run(cmd *cobra.Command, _ []string) error { | ||
currentDiskConf, err := readDiskConfig(c.globalState) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
currentJSONConfig := cloudapi.Config{} | ||
currentJSONConfigRaw := currentDiskConf.Collectors["cloud"] | ||
if currentJSONConfigRaw != nil { | ||
// We only want to modify this config, see comment below | ||
if jsonerr := json.Unmarshal(currentJSONConfigRaw, ¤tJSONConfig); jsonerr != nil { | ||
return jsonerr | ||
} | ||
} | ||
|
||
// We want to use this fully consolidated config for things like | ||
// host addresses, so users can overwrite them with env vars. | ||
consolidatedCurrentConfig, warn, err := cloudapi.GetConsolidatedConfig( | ||
currentJSONConfigRaw, c.globalState.Env, "", nil, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if warn != "" { | ||
c.globalState.Logger.Warn(warn) | ||
} | ||
|
||
// But we don't want to save them back to the JSON file, we only | ||
// want to save what already existed there and the login details. | ||
newCloudConf := currentJSONConfig | ||
|
||
show := getNullBool(cmd.Flags(), "show") | ||
reset := getNullBool(cmd.Flags(), "reset") | ||
token := getNullString(cmd.Flags(), "token") | ||
switch { | ||
case reset.Valid: | ||
newCloudConf.Token = null.StringFromPtr(nil) | ||
printToStdout(c.globalState, " token reset\n") | ||
case show.Bool: | ||
case token.Valid: | ||
newCloudConf.Token = token | ||
default: | ||
form := ui.Form{ | ||
Banner: "Please enter your Grafana Cloud k6 credentials", | ||
Fields: []ui.Field{ | ||
ui.StringField{ | ||
Key: "Email", | ||
Label: "Email", | ||
}, | ||
ui.PasswordField{ | ||
Key: "Password", | ||
Label: "Password", | ||
}, | ||
}, | ||
} | ||
if !term.IsTerminal(int(syscall.Stdin)) { //nolint:unconvert | ||
c.globalState.Logger.Warn("Stdin is not a terminal, falling back to plain text input") | ||
} | ||
var vals map[string]string | ||
vals, err = form.Run(c.globalState.Stdin, c.globalState.Stdout) | ||
if err != nil { | ||
return err | ||
} | ||
email := vals["Email"] | ||
password := vals["Password"] | ||
|
||
client := cloudapi.NewClient( | ||
c.globalState.Logger, | ||
"", | ||
consolidatedCurrentConfig.Host.String, | ||
consts.Version, | ||
consolidatedCurrentConfig.Timeout.TimeDuration()) | ||
|
||
var res *cloudapi.LoginResponse | ||
res, err = client.Login(email, password) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if res.Token == "" { | ||
return errors.New("your account does not appear to have an active API token, please consult the " + | ||
"Grafana Cloud k6 documentation for instructions on how to generate " + | ||
"one: https://grafana.com/docs/grafana-cloud/testing/k6/author-run/tokens-and-cli-authentication") | ||
} | ||
|
||
newCloudConf.Token = null.StringFrom(res.Token) | ||
} | ||
|
||
if currentDiskConf.Collectors == nil { | ||
currentDiskConf.Collectors = make(map[string]json.RawMessage) | ||
} | ||
currentDiskConf.Collectors["cloud"], err = json.Marshal(newCloudConf) | ||
if err != nil { | ||
return err | ||
} | ||
if err := writeDiskConfig(c.globalState, currentDiskConf); err != nil { | ||
return err | ||
} | ||
|
||
if newCloudConf.Token.Valid { | ||
valueColor := getColor(c.globalState.Flags.NoColor || !c.globalState.Stdout.IsTTY, color.FgCyan) | ||
if !c.globalState.Flags.Quiet { | ||
printToStdout(c.globalState, fmt.Sprintf(" token: %s\n", valueColor.Sprint(newCloudConf.Token.String))) | ||
} | ||
printToStdout(c.globalState, fmt.Sprintf( | ||
"Logged in successfully, token saved in %s\n", c.globalState.Flags.ConfigFilePath, | ||
)) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
"go.k6.io/k6/cmd/state" | ||
) | ||
|
||
const cloudRunCommandName string = "run" | ||
|
||
func getCmdCloudRun(gs *state.GlobalState) *cobra.Command { | ||
deprecatedCloudCmd := &cmdCloud{ | ||
gs: gs, | ||
showCloudLogs: true, | ||
exitOnRunning: false, | ||
uploadOnly: false, | ||
} | ||
|
||
exampleText := getExampleText(gs, ` | ||
# Run a test script in Grafana Cloud k6 | ||
$ {{.}} cloud run script.js | ||
# Run a test archive in Grafana Cloud k6 | ||
$ {{.}} cloud run archive.tar | ||
# Read a test script or archive from stdin and run it in Grafana Cloud k6 | ||
$ {{.}} cloud run - < script.js`[1:]) | ||
|
||
cloudRunCmd := &cobra.Command{ | ||
Use: cloudRunCommandName, | ||
Short: "Run a test in Grafana Cloud k6", | ||
Long: `Run a test in Grafana Cloud k6. | ||
This will archive test script(s), including all necessary resources, and execute the test in the Grafana Cloud k6 | ||
service. Using this command requires to be authenticated against Grafana Cloud k6. | ||
Use the "k6 cloud login" command to authenticate.`, | ||
Example: exampleText, | ||
Args: exactArgsWithMsg(1, | ||
"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, | ||
} | ||
|
||
cloudRunCmd.Flags().SortFlags = false | ||
cloudRunCmd.Flags().AddFlagSet(deprecatedCloudCmd.flagSet()) | ||
|
||
return cloudRunCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package tests | ||
|
||
import "testing" | ||
|
||
func TestK6CloudRun(t *testing.T) { | ||
t.Parallel() | ||
runCloudTests(t, setupK6CloudRunCmd) | ||
} | ||
|
||
func setupK6CloudRunCmd(cliFlags []string) []string { | ||
return append([]string{"k6", "cloud", "run"}, append(cliFlags, "test.js")...) | ||
} |
Oops, something went wrong.