blob: e445086c5b41c11f57c05f90494405ed2e80794f [file] [log] [blame]
Serge Bazanskia3e38cf2024-07-31 14:40:04 +00001package main
2
3import (
4 "fmt"
5 "io"
6 "net"
7 "net/http"
8 "os"
9
10 "github.com/spf13/cobra"
11
12 "source.monogon.dev/metropolis/cli/metroctl/core"
13 common "source.monogon.dev/metropolis/node"
14 "source.monogon.dev/metropolis/proto/api"
15)
16
17var nodeMetricsCmd = &cobra.Command{
18 Short: "Get metrics from node",
19 Long: `Get metrics from node.
20
21Node metrics are exported in the Prometheus format, and can be collected by any
22number of metrics collection software compatible with said format.
23
24This helper tool can be used to manually fetch metrics from a node using the same
25credentials as used to manage the cluster, and is designed to be used as a
26troubleshooting tool when a proper metrics collection system has not been set up
27for the cluster.
28
29A node ID and exporter must be provided. Currently available exporters are:
30
31 - node: node_exporter metrics for the node
32 - etcd: etcd metrics, if the node is running the cluster control plane
33 - kubernetes-scheduler, kubernetes-controller-manager, kubernetes-apiserver:
34 metrics for kubernetes control plane components, if the node runs the
35 Kubernetes control plane
36 - containerd: containerd metrics, if the node is a Kubernetes worker
37
38`,
39 Use: "metrics [node-id] [exporter]",
40 Args: cobra.MinimumNArgs(2),
41 RunE: func(cmd *cobra.Command, args []string) error {
42 ctx := cmd.Context()
43
44 // First connect to the main management service and figure out the node's IP
45 // address.
46 cc := dialAuthenticated(ctx)
47 mgmt := api.NewManagementClient(cc)
48 nodes, err := core.GetNodes(ctx, mgmt, fmt.Sprintf("node.id == %q", args[0]))
49 if err != nil {
50 return fmt.Errorf("when getting node info: %w", err)
51 }
52
53 if len(nodes) == 0 {
54 return fmt.Errorf("no such node")
55 }
56 if len(nodes) > 1 {
57 return fmt.Errorf("expression matched more than one node")
58 }
59 n := nodes[0]
60 if n.Status == nil || n.Status.ExternalAddress == "" {
61 return fmt.Errorf("node has no external address")
62 }
63
64 client := http.Client{
65 Transport: newAuthenticatedNodeHTTPTransport(ctx, n.Id),
66 }
67 res, err := client.Get(fmt.Sprintf("https://%s/metrics/%s", net.JoinHostPort(n.Status.ExternalAddress, common.MetricsPort.PortString()), args[1]))
68 if err != nil {
69 return fmt.Errorf("metrics HTTP request failed: %v", err)
70 }
71 defer res.Body.Close()
72 _, err = io.Copy(os.Stdout, res.Body)
73 return err
74 },
75}
76
77func init() {
78 nodeCmd.AddCommand(nodeMetricsCmd)
79}