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