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
19 const (
20
21 showLinkerd = "linkerd"
22
23
24 showGrafana = "grafana"
25
26
27 showURL = "url"
28
29
30 webDeployment = "web"
31
32
33 webPort = 8084
34
35
36 defaultHost = "localhost"
37
38
39 defaultPort = 50750
40 )
41
42
43
44 type dashboardOptions struct {
45 host string
46 port int
47 show string
48 wait time.Duration
49 }
50
51
52
53
54
55
56
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
67
68
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
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
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
165 }
166
167 <-portforward.GetStop()
168 return nil
169 },
170 }
171
172
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