Skip to content

Commit

Permalink
feat: allow to run local dashboard (#817)
Browse files Browse the repository at this point in the history
* feat: allow to run local dashboard

* feat: forward ports only for local dashboard

* refactor: use a helper method to execute command

Co-authored-by: Vladislav Sukhin <[email protected]>
  • Loading branch information
vsukhin and vsukhin authored Jan 24, 2022
1 parent 23521e1 commit ee33e94
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 11 deletions.
13 changes: 8 additions & 5 deletions cmd/kubectl-testkube/commands/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
)

const (
ApiServerName string = "testkube-api-server"
ApiServerPort int = 8088
ApiVersion string = "v1"
DashboardURI string = "http://dashboard.testkube.io/apiEndpoint?apiEndpoint="
CurrentApiVersion string = apiclient.Version
ApiServerName string = "testkube-api-server"
ApiServerPort int = 8088
ApiVersion string = "v1"
DashboardURI string = "http://dashboard.testkube.io"
CurrentApiVersion string = apiclient.Version
DashboardName string = "testkube-dashboard"
DashboardPort int = 80
DashboardLocalPort int = 8080
)
60 changes: 54 additions & 6 deletions cmd/kubectl-testkube/commands/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package commands

import (
"fmt"
"os/exec"
"runtime"
"sync"

"github.com/kubeshop/testkube/pkg/process"
"github.com/kubeshop/testkube/pkg/ui"
"github.com/spf13/cobra"
)

// NewDashboardCmd is a method to create new dashboard command
func NewDashboardCmd() *cobra.Command {
var namespace string
var (
namespace string
useGlobalDashboard bool
)

cmd := &cobra.Command{
Use: "dashboard",
Short: "Open testkube dashboard",
Expand All @@ -20,23 +27,50 @@ func NewDashboardCmd() *cobra.Command {
ui.Verbose = true
ui.Logo()

dashboardAddress := fmt.Sprintf("%slocalhost:%d/%s", DashboardURI, ApiServerPort, ApiVersion)
uri := fmt.Sprintf("http://localhost:%d", DashboardLocalPort)
if useGlobalDashboard {
uri = DashboardURI
}

dashboardAddress := fmt.Sprintf("%s/apiEndpoint?apiEndpoint=localhost:%d/%s", uri, ApiServerPort, ApiVersion)
ui.Success("The dashboard is accessible here:", dashboardAddress)
ui.Success("Port forwarding is started for the test results endpoint, hit Ctrl+C to stop")

var (
wg sync.WaitGroup
command *exec.Cmd
err error
)

if !useGlobalDashboard {
wg.Add(1)
go func() {
defer wg.Done()
command, err = forwardKubernetesPort(true, namespace, DashboardName, DashboardLocalPort, DashboardPort)
}()

wg.Wait()
ui.ExitOnError("port forwarding dashboard endpoint", err)
}

openCmd, err := getOpenCommand()
if err == nil {
_, err = process.Execute(openCmd, dashboardAddress)
ui.PrintOnError("openning dashboard", err)
}
_, err = process.Execute("kubectl", "port-forward",
"--namespace", namespace,
fmt.Sprintf("deployment/%s", ApiServerName),
fmt.Sprintf("%d:%d", ApiServerPort, ApiServerPort))

_, err = forwardKubernetesPort(false, namespace, ApiServerName, ApiServerPort, ApiServerPort)
ui.PrintOnError("port forwarding results endpoint", err)

if command != nil {
err = command.Process.Kill()
ui.ExitOnError("process killing", err)
}
},
}

cmd.Flags().StringVarP(&namespace, "namespace", "s", "testkube", "namespace where the testkube is installed")
cmd.Flags().BoolVar(&useGlobalDashboard, "use-global-dashboard", false, "use global dashboard for viewing testkube results")
return cmd
}

Expand All @@ -53,3 +87,17 @@ func getOpenCommand() (string, error) {
return "", fmt.Errorf("Unsupported OS")
}
}

func forwardKubernetesPort(isAsync bool, namespace, deploymentName string, localPort, clusterPort int) (
command *exec.Cmd, err error) {
fullDeploymentName := fmt.Sprintf("deployment/%s", deploymentName)
ports := fmt.Sprintf("%d:%d", localPort, clusterPort)

if isAsync {
command, err = process.ExecuteAsync("kubectl", "port-forward", "--namespace", namespace, fullDeploymentName, ports)
} else {
_, err = process.Execute("kubectl", "port-forward", "--namespace", namespace, fullDeploymentName, ports)
}

return
}
21 changes: 21 additions & 0 deletions pkg/process/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func Execute(command string, arguments ...string) (out []byte, err error) {
return ExecuteInDir("", command, arguments...)
}

// ExecuteInDir runs system command and returns whole output also in case of error in a specific directory
func ExecuteInDir(dir string, command string, arguments ...string) (out []byte, err error) {
cmd := exec.Command(command, arguments...)
if dir != "" {
Expand All @@ -31,6 +32,7 @@ func ExecuteInDir(dir string, command string, arguments ...string) (out []byte,
return buffer.Bytes(), nil
}

// LoggedExecuteInDir runs system command and returns whole output also in case of error in a specific directory with logging to writer
func LoggedExecuteInDir(dir string, writer io.Writer, command string, arguments ...string) (out []byte, err error) {
cmd := exec.Command(command, arguments...)
if dir != "" {
Expand All @@ -51,3 +53,22 @@ func LoggedExecuteInDir(dir string, writer io.Writer, command string, arguments

return buffer.Bytes(), nil
}

// ExecuteAsync runs system command and doesn't wait when it's completed
func ExecuteAsync(command string, arguments ...string) (cmd *exec.Cmd, err error) {
return ExecuteAsyncInDir("", command, arguments...)
}

// ExecuteAsyncInDir runs system command and doesn't wait when it's completed for specific directory
func ExecuteAsyncInDir(dir string, command string, arguments ...string) (cmd *exec.Cmd, err error) {
cmd = exec.Command(command, arguments...)
if dir != "" {
cmd.Dir = dir
}

if err = cmd.Start(); err != nil {
return cmd, fmt.Errorf("process error: %w", err)
}

return cmd, nil
}

0 comments on commit ee33e94

Please sign in to comment.