mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
upgrade to istio 1.19 (#1211)
Co-authored-by: CH3CHO <ch3cho@qq.com> Co-authored-by: rinfx <893383980@qq.com>
This commit is contained in:
444
hgctl/pkg/dashboard.go
Normal file
444
hgctl/pkg/dashboard.go
Normal file
@@ -0,0 +1,444 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hgctl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/hgctl/pkg/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
types2 "github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/docker"
|
||||
"github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes"
|
||||
"github.com/alibaba/higress/pkg/cmd/options"
|
||||
)
|
||||
|
||||
var (
|
||||
listenPort = 0
|
||||
promPort = 0
|
||||
grafanaPort = 0
|
||||
consolePort = 0
|
||||
controllerPort = 0
|
||||
|
||||
bindAddress = "localhost"
|
||||
|
||||
// open browser or not, default is true
|
||||
browser = true
|
||||
|
||||
// label selector
|
||||
labelSelector = ""
|
||||
|
||||
addonNamespace = ""
|
||||
|
||||
envoyDashNs = ""
|
||||
|
||||
proxyAdminPort int
|
||||
|
||||
project = "higress"
|
||||
|
||||
dockerCli = false
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPrometheusPort = 9090
|
||||
defaultGrafanaPort = 3000
|
||||
defaultConsolePort = 8080
|
||||
defaultControllerPort = 8888
|
||||
)
|
||||
|
||||
func newDashboardCmd() *cobra.Command {
|
||||
dashboardCmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
Aliases: []string{"dash", "d"},
|
||||
Short: "Access to Higress web UIs",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return fmt.Errorf("unknown dashboard %q", args[0])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
dashboardCmd.PersistentFlags().IntVarP(&listenPort, "port", "p", 0, "Local port to listen to")
|
||||
dashboardCmd.PersistentFlags().BoolVar(&browser, "browser", true,
|
||||
"When --browser is supplied as false, hgctl dashboard will not open the browser. "+
|
||||
"Default is true which means hgctl dashboard will always open a browser to view the dashboard.")
|
||||
dashboardCmd.PersistentFlags().StringVarP(&addonNamespace, "namespace", "n", "higress-system",
|
||||
"Namespace where the addon is running, if not specified, higress-system would be used")
|
||||
dashboardCmd.PersistentFlags().StringVarP(&bindAddress, "listen", "l", "localhost", "The address to bind to")
|
||||
|
||||
prom := promDashCmd()
|
||||
prom.PersistentFlags().IntVar(&promPort, "ui-port", defaultPrometheusPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(prom)
|
||||
|
||||
graf := grafanaDashCmd()
|
||||
graf.PersistentFlags().IntVar(&grafanaPort, "ui-port", defaultGrafanaPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(graf)
|
||||
|
||||
envoy := envoyDashCmd()
|
||||
envoy.PersistentFlags().StringVarP(&labelSelector, "selector", "s", "app=higress-gateway", "Label selector")
|
||||
envoy.PersistentFlags().StringVarP(&envoyDashNs, "namespace", "n", "",
|
||||
"Namespace where the addon is running, if not specified, higress-system would be used")
|
||||
envoy.PersistentFlags().IntVar(&proxyAdminPort, "ui-port", defaultProxyAdminPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(envoy)
|
||||
|
||||
consoleCmd := consoleDashCmd()
|
||||
consoleCmd.PersistentFlags().IntVar(&consolePort, "ui-port", defaultConsolePort, "The component dashboard UI port.")
|
||||
consoleCmd.PersistentFlags().BoolVar(&dockerCli, "docker", false, "Search higress console from docker")
|
||||
dashboardCmd.AddCommand(consoleCmd)
|
||||
|
||||
controllerDebugCmd := controllerDebugCmd()
|
||||
controllerDebugCmd.PersistentFlags().IntVar(&controllerPort, "ui-port", defaultControllerPort, "The component dashboard UI port.")
|
||||
dashboardCmd.AddCommand(controllerDebugCmd)
|
||||
flags := dashboardCmd.PersistentFlags()
|
||||
options.AddKubeConfigFlags(flags)
|
||||
return dashboardCmd
|
||||
}
|
||||
|
||||
// port-forward to Higress System Prometheus; open browser
|
||||
func promDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "prometheus",
|
||||
Short: "Open Prometheus web UI",
|
||||
Long: `Open Higress's Prometheus dashboard`,
|
||||
Example: ` hgctl dashboard prometheus
|
||||
|
||||
# with short syntax
|
||||
hgctl dash prometheus
|
||||
hgctl d prometheus`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app=higress-console-prometheus")
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate Prometheus pod: %v", err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no Prometheus pods found")
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Prometheus",
|
||||
"http://%s", bindAddress, promPort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// port-forward to Higress System Console; open browser
|
||||
func consoleDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "console",
|
||||
Short: "Open Console web UI",
|
||||
Long: `Open Higress Console`,
|
||||
Example: ` hgctl dashboard console
|
||||
|
||||
# with short syntax
|
||||
hgctl dash console
|
||||
hgctl d console`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if dockerCli {
|
||||
return accessDockerCompose(cmd)
|
||||
}
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
fmt.Printf("build kubernetes CLI client fail: %v\ntry to access docker container\n", err)
|
||||
return accessDockerCompose(cmd)
|
||||
}
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app.kubernetes.io/name=higress-console")
|
||||
if err != nil {
|
||||
fmt.Printf("build kubernetes CLI client fail: %v\ntry to access docker container\n", err)
|
||||
return accessDockerCompose(cmd)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
fmt.Printf("no higress console pods found\ntry to access docker container\n")
|
||||
return accessDockerCompose(cmd)
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Console",
|
||||
"http://%s", bindAddress, consolePort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// accessDockerCompose access docker compose ps
|
||||
func accessDockerCompose(cmd *cobra.Command) error {
|
||||
cli, err := docker.NewCompose(cmd.OutOrStdout())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build the docker compose client")
|
||||
}
|
||||
|
||||
list, err := cli.Ps(context.TODO(), project)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build the docker compose ps")
|
||||
}
|
||||
for _, container := range list {
|
||||
if strings.Contains(container.Service, "console") {
|
||||
// not support define ip address
|
||||
if container.Publishers != nil {
|
||||
url := fmt.Sprintf("http://localhost:%d", container.Publishers[0].PublishedPort)
|
||||
openBrowser(url, cmd.OutOrStdout(), browser)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("no higress console container found")
|
||||
}
|
||||
|
||||
// port-forward to Higress System Grafana; open browser
|
||||
func grafanaDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "grafana",
|
||||
Short: "Open Grafana web UI",
|
||||
Long: `Open Higress's Grafana dashboard`,
|
||||
Example: ` hgctl dashboard grafana
|
||||
|
||||
# with short syntax
|
||||
hgctl dash grafana
|
||||
hgctl d grafana`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app=higress-console-grafana")
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate Grafana pod: %v", err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no Grafana pods found")
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Grafana",
|
||||
"http://%s", bindAddress, grafanaPort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// port-forward to sidecar Envoy admin port; open browser
|
||||
func envoyDashCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "envoy [<type>/]<name>[.<namespace>]",
|
||||
Short: "Open Envoy admin web UI",
|
||||
Long: `Open the Envoy admin dashboard for a higress gateway`,
|
||||
Example: ` # Open Envoy dashboard for the higress-gateway-56f9b9797-b9nnc
|
||||
hgctl dashboard envoy higress-gateway-56f9b9797-b9nnc
|
||||
|
||||
# with short syntax
|
||||
hgctl dash envoy
|
||||
hgctl d envoy
|
||||
`,
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
kubeClient, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
if labelSelector == "" && len(args) < 1 {
|
||||
c.Println(c.UsageString())
|
||||
return fmt.Errorf("specify a pod or --selector")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create k8s client: %v", err)
|
||||
}
|
||||
|
||||
var podName, ns string
|
||||
if labelSelector != "" {
|
||||
pl, err := kubeClient.PodsForSelector(envoyDashNs, labelSelector)
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate pod with selector %s: %v", labelSelector, err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no pods found")
|
||||
}
|
||||
// only use the first pod in the list
|
||||
podName = pl.Items[0].Name
|
||||
ns = pl.Items[0].Namespace
|
||||
} else if len(args) > 0 {
|
||||
po, err := kubeClient.Pod(types.NamespacedName{Name: args[0], Namespace: envoyDashNs})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
podName = po.Name
|
||||
ns = po.Namespace
|
||||
}
|
||||
|
||||
return portForward(podName, ns, fmt.Sprintf("Envoy sidecar %s", podName),
|
||||
"http://%s", bindAddress, proxyAdminPort, kubeClient, c.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// port-forward to Higress System Console; open browser
|
||||
func controllerDebugCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "controller",
|
||||
Short: "Open Controller debug web UI",
|
||||
Long: `Open Higress Controller`,
|
||||
Example: ` hgctl dashboard controller
|
||||
|
||||
# with short syntax
|
||||
hgctl dash controller
|
||||
hgctl d controller`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader())
|
||||
if err != nil {
|
||||
return fmt.Errorf("build CLI client fail: %w", err)
|
||||
}
|
||||
|
||||
pl, err := client.PodsForSelector(addonNamespace, "app=higress-controller")
|
||||
if err != nil {
|
||||
return fmt.Errorf("not able to locate controller pod: %v", err)
|
||||
}
|
||||
|
||||
if len(pl.Items) < 1 {
|
||||
return errors.New("no higress controller pods found")
|
||||
}
|
||||
|
||||
// only use the first pod in the list
|
||||
return portForward(pl.Items[0].Name, addonNamespace, "Controller",
|
||||
"http://%s/debug", bindAddress, controllerPort, client, cmd.OutOrStdout(), browser)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// portForward first tries to forward localhost:remotePort to podName:remotePort, falls back to dynamic local port
|
||||
func portForward(podName, namespace, flavor, urlFormat, localAddress string, remotePort int,
|
||||
client kubernetes.CLIClient, writer io.Writer, browser bool,
|
||||
) error {
|
||||
// port preference:
|
||||
// - If --listenPort is specified, use it
|
||||
// - without --listenPort, prefer the remotePort but fall back to a random port
|
||||
var portPrefs []int
|
||||
if listenPort != 0 {
|
||||
portPrefs = []int{listenPort}
|
||||
} else {
|
||||
portPrefs = []int{remotePort}
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, localPort := range portPrefs {
|
||||
var fw kubernetes.PortForwarder
|
||||
fw, err = kubernetes.NewLocalPortForwarder(client, types.NamespacedName{Namespace: namespace, Name: podName}, localPort, remotePort, bindAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not build port forwarder for %s: %v", flavor, err)
|
||||
}
|
||||
|
||||
if err := fw.Start(); err != nil {
|
||||
fw.Stop()
|
||||
// Try the next port
|
||||
continue
|
||||
}
|
||||
|
||||
// Close the port forwarder when the command is terminated.
|
||||
ClosePortForwarderOnInterrupt(fw)
|
||||
|
||||
openBrowser(fmt.Sprintf(urlFormat, fw.Address()), writer, browser)
|
||||
|
||||
// Wait for stop
|
||||
fw.WaitForStop()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure running port forward process: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClosePortForwarderOnInterrupt(fw kubernetes.PortForwarder) {
|
||||
go func() {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt)
|
||||
defer signal.Stop(signals)
|
||||
<-signals
|
||||
fw.Stop()
|
||||
}()
|
||||
}
|
||||
|
||||
func openBrowser(url string, writer io.Writer, browser bool) {
|
||||
fmt.Fprintf(writer, "%s\n", url)
|
||||
|
||||
if !browser {
|
||||
fmt.Fprint(writer, "skipping opening a browser")
|
||||
return
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
openCommand(writer, "xdg-open", url)
|
||||
case "windows":
|
||||
openCommand(writer, "rundll32", "url.dll,FileProtocolHandler", url)
|
||||
case "darwin":
|
||||
openCommand(writer, "open", url)
|
||||
default:
|
||||
fmt.Fprintf(writer, "Unsupported platform %q; open %s in your browser.\n", runtime.GOOS, url)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func openCommand(writer io.Writer, command string, args ...string) {
|
||||
_, err := exec.LookPath(command)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
fmt.Fprintf(writer, "Could not open your browser. Please open it manually.\n")
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\nError: %s\n", args[0], err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = exec.Command(command, args...).Start()
|
||||
if err != nil {
|
||||
fmt.Fprintf(writer, "Failed to open browser; open %s in your browser.\nError: %s\n", args[0], err.Error())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user