linkerd2/cli/cmd/metrics.go

120 lines
3.1 KiB
Go

package cmd
import (
"bytes"
"fmt"
"time"
pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/spf13/cobra"
)
type metricsOptions struct {
namespace string
pod string
obfuscate bool
}
func newMetricsOptions() *metricsOptions {
return &metricsOptions{
pod: "",
obfuscate: false,
}
}
func newCmdMetrics() *cobra.Command {
options := newMetricsOptions()
cmd := &cobra.Command{
Use: "proxy-metrics [flags] (RESOURCE)",
Short: "Fetch metrics directly from Linkerd proxies",
Long: `Fetch metrics directly from Linkerd proxies.
This command initiates a port-forward to a given pod or set of pods, and
queries the /metrics endpoint on the Linkerd proxies.
The RESOURCE argument specifies the target resource to query metrics for:
(TYPE/NAME)
Examples:
* cronjob/my-cronjob
* deploy/my-deploy
* ds/my-daemonset
* job/my-job
* po/mypod1
* rc/my-replication-controller
* sts/my-statefulset
Valid resource types include:
* cronjobs
* daemonsets
* deployments
* jobs
* pods
* replicasets
* replicationcontrollers
* statefulsets`,
Example: ` # Get metrics from pod-foo-bar in the default namespace.
linkerd diagnostics proxy-metrics po/pod-foo-bar
# Get metrics from the web deployment in the emojivoto namespace.
linkerd diagnostics proxy-metrics -n emojivoto deploy/web
# Get metrics from the linkerd-destination pod in the linkerd namespace.
linkerd diagnostics proxy-metrics -n linkerd $(
kubectl --namespace linkerd get pod \
--selector linkerd.io/control-plane-component=destination \
--output name
)`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if options.namespace == "" {
options.namespace = pkgcmd.GetDefaultNamespace(kubeconfigPath, kubeContext)
}
k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
if err != nil {
return err
}
pods, err := k8s.GetPodsFor(cmd.Context(), k8sAPI, options.namespace, args[0])
if err != nil {
return err
}
results := getMetrics(k8sAPI, pods, k8s.ProxyAdminPortName, 30*time.Second, verbose)
var buf bytes.Buffer
for i, result := range results {
content := fmt.Sprintf("#\n# POD %s (%d of %d)\n#\n", result.pod, i+1, len(results))
switch {
case result.err != nil:
content += fmt.Sprintf("# ERROR: %s\n", result.err)
case options.obfuscate:
obfuscatedMetrics, err := obfuscateMetrics(result.metrics)
if err != nil {
content += fmt.Sprintf("# ERROR %s\n", err)
} else {
content += string(obfuscatedMetrics)
}
default:
content += string(result.metrics)
}
buf.WriteString(content)
}
fmt.Printf("%s", buf.String())
return nil
},
}
cmd.PersistentFlags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace of resource")
cmd.PersistentFlags().BoolVar(&options.obfuscate, "obfuscate", options.obfuscate, "Obfuscate sensitive information")
pkgcmd.ConfigureNamespaceFlagCompletion(cmd, []string{"namespace"},
kubeconfigPath, impersonate, impersonateGroup, kubeContext)
return cmd
}