-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add start of elastic-agent diagnostics command (#28265)
This PR starts the elastic-agent diagnostics command. The beats info ("/") HTTP endpoint has been changed to add more data about the running beat including git commit and ephemeral ID. Currently the diagnostics command will gather beats metadata information from the endpoint and display them along with agent version information.
- Loading branch information
1 parent
2fcd0c4
commit 887e40a
Showing
18 changed files
with
917 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
// or more contributor license agreements. Licensed under the Elastic License; | ||
// you may not use this file except in compliance with the Elastic License. | ||
|
||
package cmd | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"os" | ||
"text/tabwriter" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/client" | ||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" | ||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" | ||
) | ||
|
||
var diagOutputs = map[string]outputter{ | ||
"human": humanDiagnosticsOutput, | ||
"json": jsonOutput, | ||
"yaml": yamlOutput, | ||
} | ||
|
||
func newDiagnosticsCommand(_ []string, streams *cli.IOStreams) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "diagnostics", | ||
Short: "Gather diagnostics information from the elastic-agent and running processes.", | ||
Long: "Gather diagnostics information from the elastic-agent and running processes.", | ||
Run: func(c *cobra.Command, args []string) { | ||
if err := diagnosticCmd(streams, c, args); err != nil { | ||
fmt.Fprintf(streams.Err, "Error: %v\n%s\n", err, troubleshootMessage()) | ||
os.Exit(1) | ||
} | ||
}, | ||
} | ||
|
||
cmd.Flags().String("output", "human", "Output the diagnostics information in either human, json, or yaml (default: human)") | ||
|
||
return cmd | ||
} | ||
|
||
// DiagnosticsInfo a struct to track all inforation related to diagnostics for the agent. | ||
type DiagnosticsInfo struct { | ||
ProcMeta []client.ProcMeta | ||
AgentVersion client.Version | ||
} | ||
|
||
func diagnosticCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error { | ||
err := tryContainerLoadPaths() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
output, _ := cmd.Flags().GetString("output") | ||
outputFunc, ok := diagOutputs[output] | ||
if !ok { | ||
return fmt.Errorf("unsupported output: %s", output) | ||
} | ||
|
||
ctx := handleSignal(context.Background()) | ||
innerCtx, cancel := context.WithTimeout(ctx, 30*time.Second) | ||
defer cancel() | ||
|
||
diag, err := getDiagnostics(innerCtx) | ||
if err == context.DeadlineExceeded { | ||
return errors.New("timed out after 30 seconds trying to connect to Elastic Agent daemon") | ||
} else if err == context.Canceled { | ||
return nil | ||
} else if err != nil { | ||
return fmt.Errorf("failed to communicate with Elastic Agent daemon: %s", err) | ||
} | ||
|
||
return outputFunc(streams.Out, diag) | ||
} | ||
|
||
func getDiagnostics(ctx context.Context) (DiagnosticsInfo, error) { | ||
daemon := client.New() | ||
diag := DiagnosticsInfo{} | ||
err := daemon.Connect(ctx) | ||
if err != nil { | ||
return DiagnosticsInfo{}, err | ||
} | ||
defer daemon.Disconnect() | ||
|
||
bv, err := daemon.ProcMeta(ctx) | ||
if err != nil { | ||
return DiagnosticsInfo{}, err | ||
} | ||
diag.ProcMeta = bv | ||
|
||
version, err := daemon.Version(ctx) | ||
if err != nil { | ||
return DiagnosticsInfo{}, err | ||
} | ||
diag.AgentVersion = version | ||
|
||
return diag, nil | ||
} | ||
|
||
func humanDiagnosticsOutput(w io.Writer, obj interface{}) error { | ||
diag, ok := obj.(DiagnosticsInfo) | ||
if !ok { | ||
return fmt.Errorf("unable to cast %T as DiagnosticsInfo", obj) | ||
} | ||
return outputDiagnostics(w, diag) | ||
} | ||
|
||
func outputDiagnostics(w io.Writer, d DiagnosticsInfo) error { | ||
tw := tabwriter.NewWriter(w, 4, 1, 2, ' ', 0) | ||
fmt.Fprintf(tw, "elastic-agent\tversion: %s\n", d.AgentVersion.Version) | ||
fmt.Fprintf(tw, "\tbuild_commit: %s\tbuild_time: %s\tsnapshot_build: %v\n", d.AgentVersion.Commit, d.AgentVersion.BuildTime, d.AgentVersion.Snapshot) | ||
if len(d.ProcMeta) == 0 { | ||
fmt.Fprintf(tw, "Applications: (none)\n") | ||
} else { | ||
fmt.Fprintf(tw, "Applications:\n") | ||
for _, app := range d.ProcMeta { | ||
fmt.Fprintf(tw, " *\tname: %s\troute_key: %s\n", app.Name, app.RouteKey) | ||
if app.Error != "" { | ||
fmt.Fprintf(tw, "\terror: %s\n", app.Error) | ||
} else { | ||
fmt.Fprintf(tw, "\tprocess: %s\tid: %s\tephemeral_id: %s\telastic_license: %v\n", app.Process, app.ID, app.EphemeralID, app.ElasticLicensed) | ||
fmt.Fprintf(tw, "\tversion: %s\tcommit: %s\tbuild_time: %s\tbinary_arch: %v\n", app.Version, app.BuildCommit, app.BuildTime, app.BinaryArchitecture) | ||
fmt.Fprintf(tw, "\thostname: %s\tusername: %s\tuser_id: %s\tuser_gid: %s\n", app.Hostname, app.Username, app.UserID, app.UserGID) | ||
} | ||
|
||
} | ||
} | ||
tw.Flush() | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
// or more contributor license agreements. Licensed under the Elastic License; | ||
// you may not use this file except in compliance with the Elastic License. | ||
|
||
package cmd | ||
|
||
import ( | ||
"os" | ||
"time" | ||
|
||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/control/client" | ||
) | ||
|
||
var testDiagnostics = DiagnosticsInfo{ | ||
AgentVersion: client.Version{ | ||
Version: "test-version", | ||
Commit: "test-commit", | ||
BuildTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), | ||
Snapshot: false, | ||
}, | ||
ProcMeta: []client.ProcMeta{client.ProcMeta{ | ||
Process: "filebeat", | ||
Name: "filebeat", | ||
Hostname: "test-host", | ||
ID: "filebeat-id", | ||
EphemeralID: "filebeat-ephemeral-id", | ||
Version: "filebeat-version", | ||
BuildCommit: "filebeat-commit", | ||
BuildTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), | ||
Username: "test-user", | ||
UserID: "1000", | ||
UserGID: "1000", | ||
BinaryArchitecture: "test-architecture", | ||
RouteKey: "test", | ||
ElasticLicensed: true, | ||
}, client.ProcMeta{ | ||
Process: "filebeat", | ||
Name: "filebeat_monitoring", | ||
Hostname: "test-host", | ||
ID: "filebeat_monitoring-id", | ||
EphemeralID: "filebeat_monitoring-ephemeral-id", | ||
Version: "filebeat_monitoring-version", | ||
BuildCommit: "filebeat_monitoring-commit", | ||
BuildTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), | ||
Username: "test-user", | ||
UserID: "1000", | ||
UserGID: "1000", | ||
BinaryArchitecture: "test-architecture", | ||
RouteKey: "test", | ||
ElasticLicensed: true, | ||
}, client.ProcMeta{ | ||
Name: "metricbeat", | ||
RouteKey: "test", | ||
Error: "failed to get metricbeat data", | ||
}}, | ||
} | ||
|
||
func Example_humanDiagnosticsOutput() { | ||
humanDiagnosticsOutput(os.Stdout, testDiagnostics) | ||
// Output: | ||
// elastic-agent version: test-version | ||
// build_commit: test-commit build_time: 2021-01-01 00:00:00 +0000 UTC snapshot_build: false | ||
// Applications: | ||
// * name: filebeat route_key: test | ||
// process: filebeat id: filebeat-id ephemeral_id: filebeat-ephemeral-id elastic_license: true | ||
// version: filebeat-version commit: filebeat-commit build_time: 2021-01-01 00:00:00 +0000 UTC binary_arch: test-architecture | ||
// hostname: test-host username: test-user user_id: 1000 user_gid: 1000 | ||
// * name: filebeat_monitoring route_key: test | ||
// process: filebeat id: filebeat_monitoring-id ephemeral_id: filebeat_monitoring-ephemeral-id elastic_license: true | ||
// version: filebeat_monitoring-version commit: filebeat_monitoring-commit build_time: 2021-01-01 00:00:00 +0000 UTC binary_arch: test-architecture | ||
// hostname: test-host username: test-user user_id: 1000 user_gid: 1000 | ||
// * name: metricbeat route_key: test | ||
// error: failed to get metricbeat data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.