...

Source file src/github.com/linkerd/linkerd2/viz/cmd/dashboard.go

Documentation: github.com/linkerd/linkerd2/viz/cmd

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"time"
     9  
    10  	"github.com/linkerd/linkerd2/pkg/healthcheck"
    11  	"github.com/linkerd/linkerd2/pkg/k8s"
    12  	"github.com/linkerd/linkerd2/viz/pkg/api"
    13  	hc "github.com/linkerd/linkerd2/viz/pkg/healthcheck"
    14  	"github.com/pkg/browser"
    15  	"github.com/spf13/cobra"
    16  )
    17  
    18  // These constants are used by the `show` flag.
    19  const (
    20  	// showLinkerd opens the Linkerd dashboard in a web browser (default).
    21  	showLinkerd = "linkerd"
    22  
    23  	// showGrafana opens the Grafana dashboard in a web browser.
    24  	showGrafana = "grafana"
    25  
    26  	// showURL displays dashboard URLs without opening a browser.
    27  	showURL = "url"
    28  
    29  	// webDeployment is the name of the web deployment in cli/install/template.go
    30  	webDeployment = "web"
    31  
    32  	// webPort is the http port from the web pod spec in cli/install/template.go
    33  	webPort = 8084
    34  
    35  	// defaultHost is the default host used for port-forwarding via `linkerd dashboard`
    36  	defaultHost = "localhost"
    37  
    38  	// defaultPort is for port-forwarding via `linkerd dashboard`
    39  	defaultPort = 50750
    40  )
    41  
    42  // dashboardOptions holds values for command line flags that apply to the dashboard
    43  // command.
    44  type dashboardOptions struct {
    45  	host string
    46  	port int
    47  	show string
    48  	wait time.Duration
    49  }
    50  
    51  // newDashboardOptions initializes dashboard options with default
    52  // values for host, port, and which dashboard to show. Also, set
    53  // max wait time duration for 300 seconds for the dashboard to
    54  // become available
    55  //
    56  // These options may be overridden on the CLI at run-time
    57  func newDashboardOptions() *dashboardOptions {
    58  	return &dashboardOptions{
    59  		host: defaultHost,
    60  		port: defaultPort,
    61  		show: showLinkerd,
    62  		wait: 300 * time.Second,
    63  	}
    64  }
    65  
    66  // NewCmdDashboard creates a new cobra command `dashboard` which contains commands for visualizing linkerd's dashboards.
    67  // After validating flag values, it will use the Kubernetes API to portforward requests to the Grafana and Web Deployments
    68  // until the process gets killed/canceled
    69  func NewCmdDashboard() *cobra.Command {
    70  	options := newDashboardOptions()
    71  
    72  	cmd := &cobra.Command{
    73  		Use:   "dashboard [flags]",
    74  		Short: "Open the Linkerd dashboard in a web browser",
    75  		Args:  cobra.NoArgs,
    76  		RunE: func(cmd *cobra.Command, args []string) error {
    77  			if options.port < 0 {
    78  				return fmt.Errorf("port must be greater than or equal to zero, was %d", options.port)
    79  			}
    80  
    81  			if options.show != showLinkerd && options.show != showGrafana && options.show != showURL {
    82  				return fmt.Errorf("unknown value for 'show' param, was: %s, must be one of: %s, %s, %s",
    83  					options.show, showLinkerd, showGrafana, showURL)
    84  			}
    85  
    86  			// ensure we can connect to the viz API before starting the proxy
    87  			api.CheckClientOrRetryOrExit(hc.VizOptions{
    88  				Options: &healthcheck.Options{
    89  					ControlPlaneNamespace: controlPlaneNamespace,
    90  					KubeConfig:            kubeconfigPath,
    91  					Impersonate:           impersonate,
    92  					ImpersonateGroup:      impersonateGroup,
    93  					KubeContext:           kubeContext,
    94  					APIAddr:               apiAddr,
    95  					RetryDeadline:         time.Now().Add(options.wait),
    96  				},
    97  				VizNamespaceOverride: vizNamespace,
    98  			}, true)
    99  
   100  			k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
   101  			if err != nil {
   102  				return err
   103  			}
   104  
   105  			vizNs, err := k8sAPI.GetNamespaceWithExtensionLabel(context.Background(), ExtensionName)
   106  			if err != nil {
   107  				return err
   108  			}
   109  
   110  			signals := make(chan os.Signal, 1)
   111  			signal.Notify(signals, os.Interrupt)
   112  			defer signal.Stop(signals)
   113  
   114  			portforward, err := k8s.NewPortForward(
   115  				cmd.Context(),
   116  				k8sAPI,
   117  				vizNs.Name,
   118  				webDeployment,
   119  				options.host,
   120  				options.port,
   121  				webPort,
   122  				verbose,
   123  			)
   124  			if err != nil {
   125  				fmt.Fprintf(os.Stderr, "Failed to initialize port-forward: %s\n", err)
   126  				os.Exit(1)
   127  			}
   128  
   129  			if err = portforward.Init(); err != nil {
   130  				// TODO: consider falling back to an ephemeral port if defaultPort is taken
   131  				fmt.Fprintf(os.Stderr, "Error running port-forward: %s\nCheck for `linkerd dashboard` running in other terminal sessions, or use the `--port` flag.\n", err)
   132  				os.Exit(1)
   133  			}
   134  
   135  			go func() {
   136  				<-signals
   137  				portforward.Stop()
   138  			}()
   139  
   140  			webURL := portforward.URLFor("")
   141  			grafanaURL := portforward.URLFor("/grafana")
   142  
   143  			fmt.Printf("Linkerd dashboard available at:\n%s\n", webURL)
   144  			fmt.Printf("Grafana dashboard available at:\n%s\n", grafanaURL)
   145  
   146  			switch options.show {
   147  			case showLinkerd:
   148  				fmt.Println("Opening Linkerd dashboard in the default browser")
   149  
   150  				err = browser.OpenURL(webURL)
   151  				if err != nil {
   152  					fmt.Fprintln(os.Stderr, "Failed to open Linkerd dashboard automatically")
   153  					fmt.Fprintf(os.Stderr, "Visit %s in your browser to view the dashboard\n", webURL)
   154  				}
   155  			case showGrafana:
   156  				fmt.Println("Opening Grafana dashboard in the default browser")
   157  
   158  				err = browser.OpenURL(grafanaURL)
   159  				if err != nil {
   160  					fmt.Fprintln(os.Stderr, "Failed to open Grafana dashboard automatically")
   161  					fmt.Fprintf(os.Stderr, "Visit %s in your browser to view the dashboard\n", grafanaURL)
   162  				}
   163  			case showURL:
   164  				// no-op, we already printed the URLs
   165  			}
   166  
   167  			<-portforward.GetStop()
   168  			return nil
   169  		},
   170  	}
   171  
   172  	// This is identical to what `kubectl proxy --help` reports, `--port 0` indicates a random port.
   173  	cmd.PersistentFlags().StringVar(&options.host, "address", options.host, "The address at which to serve requests")
   174  	cmd.PersistentFlags().IntVarP(&options.port, "port", "p", options.port, "The local port on which to serve requests (when set to 0, a random port will be used)")
   175  	cmd.PersistentFlags().StringVar(&options.show, "show", options.show, "Open a dashboard in a browser or show URLs in the CLI (one of: linkerd, grafana, url)")
   176  	cmd.PersistentFlags().DurationVar(&options.wait, "wait", options.wait, "Wait for dashboard to become available if it's not available when the command is run")
   177  
   178  	return cmd
   179  }
   180  

View as plain text