github.com/kubeshop/testkube@v1.17.23/cmd/kubectl-testkube/commands/dashboard.go (about) 1 package commands 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "os" 8 "os/signal" 9 "syscall" 10 11 "github.com/pkg/errors" 12 "github.com/skratchdot/open-golang/open" 13 "github.com/spf13/cobra" 14 15 "github.com/kubeshop/testkube/cmd/kubectl-testkube/config" 16 "github.com/kubeshop/testkube/pkg/k8sclient" 17 "github.com/kubeshop/testkube/pkg/ui" 18 ) 19 20 const maxPortNumber = 65535 21 22 // NewDashboardCmd is a method to create new dashboard command 23 func NewDashboardCmd() *cobra.Command { 24 var namespace string 25 var verbose bool 26 27 cmd := &cobra.Command{ 28 Use: "dashboard", 29 Aliases: []string{"d", "open-dashboard", "ui"}, 30 Short: "Open Testkube dashboard", 31 Long: `Open Testkube dashboard`, 32 Run: func(cmd *cobra.Command, args []string) { 33 cfg, err := config.Load() 34 ui.ExitOnError("loading config file", err) 35 36 if namespace == "" { 37 namespace = cfg.Namespace 38 } 39 40 if cfg.ContextType != config.ContextTypeCloud { 41 isDashboardRunning, _ := k8sclient.IsPodOfServiceRunning(context.Background(), cfg.Namespace, config.EnterpriseUiName) 42 if isDashboardRunning { 43 openOnPremDashboard(cmd, cfg, verbose) 44 } else { 45 ui.Warn("No dashboard found. Is it running in the " + namespace + " namespace?") 46 } 47 } else { 48 openCloudDashboard(cfg) 49 } 50 }, 51 } 52 53 cmd.Flags().BoolVarP(&verbose, "verbose", "", false, "show additional debug messages") 54 cmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to install "+demoInstallationName) 55 56 return cmd 57 } 58 59 func openCloudDashboard(cfg config.Data) { 60 // open browser 61 uri := fmt.Sprintf("%s/organization/%s/environment/%s", cfg.CloudContext.UiUri, cfg.CloudContext.OrganizationId, cfg.CloudContext.EnvironmentId) 62 err := open.Run(uri) 63 ui.PrintOnError("openning dashboard", err) 64 } 65 66 func openOnPremDashboard(cmd *cobra.Command, cfg config.Data, verbose bool) { 67 uiLocalPort, err := getDashboardLocalPort(config.EnterpriseApiForwardingPort) 68 ui.PrintOnError("getting an ui forwarding available port", err) 69 uri := fmt.Sprintf("http://localhost:%d", uiLocalPort) 70 71 ctx, cancel := context.WithCancel(context.Background()) 72 err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseApiName, config.EnterpriseApiPort, config.EnterpriseApiForwardingPort, verbose) 73 ui.PrintOnError("port forwarding api", err) 74 err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseUiName, config.EnterpriseUiPort, uiLocalPort, verbose) 75 ui.PrintOnError("port forwarding ui", err) 76 err = k8sclient.PortForward(ctx, cfg.Namespace, config.EnterpriseDexName, config.EnterpriseDexPort, config.EnterpriseDexForwardingPort, verbose) 77 ui.PrintOnError("port forwarding dex", err) 78 79 err = open.Run(uri) 80 ui.ExitOnError("opening dashboard in browser", err) 81 c := make(chan os.Signal, 1) 82 signal.Notify(c, os.Interrupt, syscall.SIGTERM) 83 84 ui.Success("The dashboard is accessible here:", uri) 85 ui.Success("Port forwarding the necessary services, hit Ctrl+c (or Cmd+c) to stop") 86 <-c 87 cancel() 88 89 } 90 91 func localPortCheck(port int) error { 92 ln, err := net.Listen("tcp", ":"+fmt.Sprint(port)) 93 if err != nil { 94 return err 95 } 96 97 ln.Close() 98 return nil 99 } 100 101 func getDashboardLocalPort(apiServerPort int) (int, error) { 102 for port := DashboardLocalPort; port <= maxPortNumber; port++ { 103 if port == apiServerPort { 104 continue 105 } 106 107 if localPortCheck(port) == nil { 108 return port, nil 109 } 110 } 111 112 return 0, errors.New("no available local port") 113 }