diff --git a/cmd/kubectl-testkube/commands/common/errors.go b/cmd/kubectl-testkube/commands/common/errors.go index 92015d9fba7..1a89bce4eda 100644 --- a/cmd/kubectl-testkube/commands/common/errors.go +++ b/cmd/kubectl-testkube/commands/common/errors.go @@ -32,8 +32,10 @@ const ( // TKErrHelmCommandFailed is returned when a helm command fails. TKErrHelmCommandFailed ErrorCode = "TKERR-1301" - // TKErrKubectlCommandFailed is returned when a kubectl command fail. + // TKErrKubectlCommandFailed is returned when a kubectl command fails. TKErrKubectlCommandFailed ErrorCode = "TKERR-1302" + // TKErrDockerCommandFailed is returned when a docker command fails. + TKErrDockerCommandFailed ErrorCode = "TKERR-1303" // TKErrCleanOldMigrationJobFailed is returned in case of issues with old migration jobs. TKErrCleanOldMigrationJobFailed ErrorCode = "TKERR-1401" diff --git a/cmd/kubectl-testkube/commands/common/helper.go b/cmd/kubectl-testkube/commands/common/helper.go index a42486e35a1..1d60a1f45c4 100644 --- a/cmd/kubectl-testkube/commands/common/helper.go +++ b/cmd/kubectl-testkube/commands/common/helper.go @@ -759,3 +759,59 @@ func UiGetNamespace(cmd *cobra.Command, defaultNamespace string) string { return namespace } + +func RunDockerCommand(args []string) (output string, cliErr *CLIError) { + out, err := process.Execute("docker", args...) + if err != nil { + return "", NewCLIError( + TKErrDockerCommandFailed, + "docker command failed", + "Check is the Docker service installed and running on your computer by executing 'docker info' ", + err, + ) + } + return string(out), nil +} + +func DockerRunTestkubeAgent(options HelmOptions, cfg config.Data, containerName string) *CLIError { + // use config if set + if cfg.CloudContext.AgentKey != "" && options.Master.AgentToken == "" { + options.Master.AgentToken = cfg.CloudContext.AgentKey + } + + if options.Master.AgentToken == "" { + return NewCLIError( + TKErrInvalidInstallConfig, + "Invalid install config", + "Provide the agent token by setting the '--agent-token' flag", + errors.New("agent key is required")) + } + + args := prepareTestkubeProDockerArgs(options, containerName) + output, err := RunDockerCommand(args) + if err != nil { + return err + } + + ui.Debug("Docker command output:") + ui.Debug("Arguments", args...) + + ui.Debug("Docker run testkube output", output) + + return nil +} + +// prepareTestkubeProDockerArgs prepares docker arguments for Testkube Pro running. +func prepareTestkubeProDockerArgs(options HelmOptions, containerName string) []string { + args := []string{ + "run", + "--name", containerName, + "--privileged", + "-d", + "-e", "CLOUD_URL=" + options.Master.URIs.Agent, + "-e", "AGENT_KEY=" + options.Master.AgentToken, + "kubeshop/testkube-agent", + } + + return args +} diff --git a/cmd/kubectl-testkube/commands/pro.go b/cmd/kubectl-testkube/commands/pro.go index 09986b4c757..4fae70e3bc4 100644 --- a/cmd/kubectl-testkube/commands/pro.go +++ b/cmd/kubectl-testkube/commands/pro.go @@ -19,6 +19,7 @@ func NewProCmd() *cobra.Command { cmd.AddCommand(pro.NewDisconnectCmd()) cmd.AddCommand(pro.NewInitCmd()) cmd.AddCommand(pro.NewLoginCmd()) + cmd.AddCommand(pro.NewDockerCmd()) return cmd } diff --git a/cmd/kubectl-testkube/commands/pro/docker.go b/cmd/kubectl-testkube/commands/pro/docker.go new file mode 100644 index 00000000000..df713018d87 --- /dev/null +++ b/cmd/kubectl-testkube/commands/pro/docker.go @@ -0,0 +1,106 @@ +package pro + +import ( + "os" + + "github.com/spf13/cobra" + + "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" + "github.com/kubeshop/testkube/cmd/kubectl-testkube/config" + "github.com/kubeshop/testkube/pkg/ui" +) + +func NewDockerCmd() *cobra.Command { + var noLogin bool // ignore ask for login + var containerName string + var options common.HelmOptions + + cmd := &cobra.Command{ + Use: "docker", + Short: "Run Testkube Docker Agent and connect to Testkube Pro environment", + Aliases: []string{"da", "docker-agent"}, + Run: func(cmd *cobra.Command, args []string) { + ui.Info("WELCOME TO") + ui.Logo() + + cfg, err := config.Load() + if err != nil { + cliErr := common.NewCLIError( + common.TKErrConfigInitFailed, + "Error loading testkube config file", + "Check is the Testkube config file (~/.testkube/config.json) accessible and has right permissions", + err, + ) + cliErr.Print() + os.Exit(1) + } + ui.NL() + + common.ProcessMasterFlags(cmd, &options, &cfg) + + sendAttemptTelemetry(cmd, cfg) + + if !options.NoConfirm { + ui.Warn("This will run Testkube Docker Agent latest version. This may take a few minutes.") + ui.Warn("Please be sure you have Docker service running before continuing!") + ui.NL() + + dockerInfo, cliErr := common.RunDockerCommand([]string{"info"}) + if cliErr != nil { + sendErrTelemetry(cmd, cfg, "docker_info", err) + common.HandleCLIError(cliErr) + } + ui.Alert("Current docker info:", dockerInfo) + ui.NL() + + ok := ui.Confirm("Do you want to continue?") + if !ok { + ui.Errf("Testkube Docker Agent running cancelled") + sendErrTelemetry(cmd, cfg, "user_cancel", err) + return + } + } + + spinner := ui.NewSpinner("Running Testkube Docker Agent") + if cliErr := common.DockerRunTestkubeAgent(options, cfg, containerName); cliErr != nil { + spinner.Fail() + sendErrTelemetry(cmd, cfg, "docker_run", cliErr) + common.HandleCLIError(cliErr) + } + + spinner.Success() + + ui.NL() + + if noLogin { + ui.Alert("Saving Testkube CLI Pro context, you need to authorize CLI through `testkube set context` later") + common.PopulateCloudConfig(cfg, "", &options) + ui.Info(" Happy Testing! 🚀") + ui.NL() + return + } + + ui.H2("Saving Testkube CLI Pro context") + var token, refreshToken string + if !common.IsUserLoggedIn(cfg, options) { + token, refreshToken, err = common.LoginUser(options.Master.URIs.Auth) + sendErrTelemetry(cmd, cfg, "login", err) + ui.ExitOnError("user login", err) + } + err = common.PopulateLoginDataToContext(options.Master.OrgId, options.Master.EnvId, token, refreshToken, options, cfg) + if err != nil { + sendErrTelemetry(cmd, cfg, "setting_context", err) + ui.ExitOnError("Setting Pro environment context", err) + } + ui.Info(" Happy Testing! 🚀") + ui.NL() + }, + } + + common.PopulateMasterFlags(cmd, &options) + + cmd.Flags().BoolVarP(&noLogin, "no-login", "", false, "Ignore login prompt, set existing token later by `testkube set context`") + cmd.Flags().StringVar(&containerName, "container-name", "testkube-agent", "container name for Testkube Docker Agent") + + return cmd +}