Skip to content
Merged
74 changes: 74 additions & 0 deletions cli/cmd/controller-metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cmd

import (
"bytes"
"fmt"
"time"

"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ControllerMetricsOptions holds values for command line flags that apply to the controller-metrics
// command.
type ControllerMetricsOptions struct {
wait time.Duration
}

// newControllerMetricsOptions initializes controller-metrics options setting
// the max wait time duration as 30 seconds to fetch controller-metrics
//
// This option may be overridden on the CLI at run-time
func newControllerMetricsOptions() *ControllerMetricsOptions {
return &ControllerMetricsOptions{
wait: 30 * time.Second,
}
}

// newCmdControllerMetrics creates a new cobra command `controller-metrics` which contains commands to fetch control plane container's metrics
func newCmdControllerMetrics() *cobra.Command {
options := newControllerMetricsOptions()

cmd := &cobra.Command{
Use: "controller-metrics",
Short: "Fetch metrics directly from the Linkerd control plane containers",
Long: `Fetch metrics directly from Linkerd control plane containers.

This command initiates port-forward to each control plane process, and
queries the /metrics endpoint on them.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
if err != nil {
return err
}

pods, err := k8sAPI.CoreV1().Pods(controlPlaneNamespace).List(cmd.Context(), metav1.ListOptions{})
if err != nil {
return err
}

results := getMetrics(k8sAPI, pods.Items, adminHTTPPortName, options.wait, verbose)

var buf bytes.Buffer
for i, result := range results {
content := fmt.Sprintf("#\n# POD %s (%d of %d)\n", result.pod, i+1, len(results))
if result.err != nil {
content += fmt.Sprintf("# ERROR %s\n", result.err)
} else {
content += fmt.Sprintf("# CONTAINER %s \n#\n", result.container)
content += string(result.metrics)
}
buf.WriteString(content)
}
fmt.Printf("%s", buf.String())

return nil
},
}

cmd.Flags().DurationVarP(&options.wait, "wait", "w", options.wait, "Time allowed to fetch diagnostics")

return cmd
}
88 changes: 26 additions & 62 deletions cli/cmd/diagnostics.go
Original file line number Diff line number Diff line change
@@ -1,78 +1,42 @@
package cmd

import (
"bytes"
"fmt"
"time"

"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
adminHTTPPortName string = "admin-http"
)

// diagnosticsOptions holds values for command line flags that apply to the diagnostics
// command.
type diagnosticsOptions struct {
wait time.Duration
}

// newDiagnosticsOptions initializes diagnostics options setting
// the max wait time duration as 30 seconds to fetch diagnostics
//
// This option may be overridden on the CLI at run-time
func newDiagnosticsOptions() *diagnosticsOptions {
return &diagnosticsOptions{
wait: 30 * time.Second,
}
}

// newCmdDashboard creates a new cobra command `diagnostics` which contains commands to fetch control plane container's metrics
// newCmdDiagnostics creates a new cobra command `diagnostics` which contains commands to fetch Linkerd diagnostics
func newCmdDiagnostics() *cobra.Command {
options := newDiagnosticsOptions()

cmd := &cobra.Command{
Use: "diagnostics",
Short: "Fetch metrics directly from the Linkerd control plane containers",
Long: `Fetch metrics directly from Linkerd control plane containers.

This command initiates port-forward to each control plane process, and
queries the /metrics endpoint on them.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
if err != nil {
return err
}

pods, err := k8sAPI.CoreV1().Pods(controlPlaneNamespace).List(cmd.Context(), metav1.ListOptions{})
if err != nil {
return err
}

results := getMetrics(k8sAPI, pods.Items, adminHTTPPortName, options.wait, verbose)

var buf bytes.Buffer
for i, result := range results {
content := fmt.Sprintf("#\n# POD %s (%d of %d)\n", result.pod, i+1, len(results))
if result.err != nil {
content += fmt.Sprintf("# ERROR %s\n", result.err)
} else {
content += fmt.Sprintf("# CONTAINER %s \n#\n", result.container)
content += string(result.metrics)
}
buf.WriteString(content)
}
fmt.Printf("%s", buf.String())

return nil
},
diagnosticsCmd := &cobra.Command{
Use: "diagnostics [flags]",
Aliases: []string{"dg"},
Args: cobra.NoArgs,
Short: "Commands used to diagnose Linkerd components",
Long: `Commands used to diagnose Linkerd components.

This command provides subcommands to diagnose the functionality of Linkerd.`,
Example: ` # Get control-plane component metrics
linkerd diagnostics cp-metrics

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cp-metrics doesn't seem like it's a valid abbreviation for this command.

Comment thread
Pothulapati marked this conversation as resolved.
Outdated

# Get metrics from the web deployment in the emojivoto namespace.
linkerd diagnostics proxy-metrics -n emojivoto deploy/web

# Get the endpoints for authorities in Linkerd's control-plane itself
linkerd diagnostics endpoints linkerd-controller-api.linkerd.svc.cluster.local:8085

# Install service profiles for the control-plane components.
linkerd diagnostics install-sp
`,
}

cmd.Flags().DurationVarP(&options.wait, "wait", "w", options.wait, "Time allowed to fetch diagnostics")
diagnosticsCmd.AddCommand(newCmdControllerMetrics())
diagnosticsCmd.AddCommand(newCmdEndpoints())
diagnosticsCmd.AddCommand(newCmdMetrics())
diagnosticsCmd.AddCommand(newCmdInstallSP())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call the install-sp something else? As its a bit confusing as this installs service profiles for the control-plane components and not the service profiles crds itself

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a better suggestion for the name of this.


return cmd
return diagnosticsCmd
}
8 changes: 4 additions & 4 deletions cli/cmd/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ func newCmdEndpoints() *cobra.Command {
options := newEndpointsOptions()

example := ` # get all endpoints for the authorities emoji-svc.emojivoto.svc.cluster.local:8080 and web-svc.emojivoto.svc.cluster.local:80
linkerd endpoints emoji-svc.emojivoto.svc.cluster.local:8080 web-svc.emojivoto.svc.cluster.local:80
linkerd diagnostics endpoints emoji-svc.emojivoto.svc.cluster.local:8080 web-svc.emojivoto.svc.cluster.local:80

# get that same information in json format
linkerd endpoints -o json emoji-svc.emojivoto.svc.cluster.local:8080 web-svc.emojivoto.svc.cluster.local:80
linkerd diagnostics endpoints -o json emoji-svc.emojivoto.svc.cluster.local:8080 web-svc.emojivoto.svc.cluster.local:80

# get the endpoints for authorities in Linkerd's control-plane itself
linkerd endpoints linkerd-controller-api.linkerd.svc.cluster.local:8085
linkerd endpoints linkerd-web.linkerd.svc.cluster.local:8084`
linkerd diagnostics endpoints linkerd-controller-api.linkerd.svc.cluster.local:8085
linkerd diagnostics endpoints linkerd-web.linkerd.svc.cluster.local:8084`

cmd := &cobra.Command{
Use: "endpoints [flags] authorities",
Expand Down
8 changes: 4 additions & 4 deletions cli/cmd/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func newCmdMetrics() *cobra.Command {
options := newMetricsOptions()

cmd := &cobra.Command{
Use: "metrics [flags] (RESOURCE)",
Use: "proxy-metrics [flags] (RESOURCE)",
Short: "Fetch metrics directly from Linkerd proxies",
Long: `Fetch metrics directly from Linkerd proxies.

Expand Down Expand Up @@ -62,13 +62,13 @@ func newCmdMetrics() *cobra.Command {
* replicationcontrollers
* statefulsets`,
Example: ` # Get metrics from pod-foo-bar in the default namespace.
linkerd metrics po/pod-foo-bar
linkerd diagnostics metrics po/pod-foo-bar

# Get metrics from the web deployment in the emojivoto namespace.
linkerd metrics -n emojivoto deploy/web
linkerd diagnostics metrics -n emojivoto deploy/web

# Get metrics from the linkerd-controller pod in the linkerd namespace.
linkerd metrics -n linkerd $(
linkerd diagnostics metrics -n linkerd $(
Comment thread
Pothulapati marked this conversation as resolved.
Outdated
kubectl --namespace linkerd get pod \
--selector linkerd.io/control-plane-component=controller \
--output name
Expand Down
3 changes: 0 additions & 3 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,10 @@ func init() {
RootCmd.AddCommand(newCmdCompletion())
RootCmd.AddCommand(newCmdDiagnostics())
RootCmd.AddCommand(newCmdDoc())
RootCmd.AddCommand(newCmdEndpoints())
RootCmd.AddCommand(newCmdIdentity())
RootCmd.AddCommand(newCmdInject())
RootCmd.AddCommand(newCmdInstall())
RootCmd.AddCommand(newCmdInstallCNIPlugin())
RootCmd.AddCommand(newCmdInstallSP())
RootCmd.AddCommand(newCmdMetrics())
RootCmd.AddCommand(newCmdProfile())
RootCmd.AddCommand(newCmdRepair())
RootCmd.AddCommand(newCmdUninject())
Expand Down
3 changes: 2 additions & 1 deletion test/integration/endpoints/endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestGoodEndpoints(t *testing.T) {
vizNs := TestHelper.GetVizNamespace()
testDataPath := "testdata"
cmd := []string{
"diagnostics",
"endpoints",
fmt.Sprintf("linkerd-controller-api.%s.svc.cluster.local:8085", ns),
fmt.Sprintf("linkerd-dst.%s.svc.cluster.local:8086", ns),
Expand Down Expand Up @@ -69,7 +70,7 @@ func TestGoodEndpoints(t *testing.T) {

// TODO: when #3004 gets fixed, add a negative test for mismatched ports
func TestBadEndpoints(t *testing.T) {
_, stderr, err := TestHelper.PipeToLinkerdRun("", "endpoints", "foo")
_, stderr, err := TestHelper.PipeToLinkerdRun("", "diagnostics", "endpoints", "foo")
if err == nil {
testutil.AnnotatedFatalf(t, "was expecting an error", "was expecting an error: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion test/integration/opaqueports/opaque_ports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestOpaquePorts(t *testing.T) {

func getPodMetrics(pod v1.Pod, ns string) (string, error) {
podName := fmt.Sprintf("pod/%s", pod.Name)
cmd := []string{"metrics", "--namespace", ns, podName}
cmd := []string{"diagnostics", "proxy-metrics", "--namespace", ns, podName}
metrics, err := TestHelper.LinkerdRun(cmd...)
if err != nil {
return "", err
Expand Down