Skip to content

Commit

Permalink
update status command
Browse files Browse the repository at this point in the history
- change to list format for human output
- change human to only report top level status if all components and
  units are healthy
- change human to report full status for component if any unit or
  component itself is not healthy
- add `human_full` which always displays full status
- sort components and units so order will always be the same

Closes #2107
Closes #2829
  • Loading branch information
leehinman committed Jun 16, 2023
1 parent 6a6720e commit 4cb6ef0
Show file tree
Hide file tree
Showing 17 changed files with 384 additions and 35 deletions.
34 changes: 34 additions & 0 deletions changelog/fragments/1686949146-update-status-command.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Kind can be one of:
# - breaking-change: a change to previously-documented behavior
# - deprecation: functionality that is being removed in a later release
# - bug-fix: fixes a problem in a previous version
# - enhancement: extends functionality but does not break or fix existing behavior
# - feature: new functionality
# - known-issue: problems that we are aware of in a given version
# - security: impacts on the security of a product or a user’s deployment.
# - upgrade: important information for someone upgrading from a prior version
# - other: does not fit into any of the other categories
kind: breaking-change

# Change summary; a 80ish characters long description of the change.
summary: status command, change human output to be a summary, added human_full output

# Long description; in case the summary is not enough to describe the change
# this field accommodate a description without length limits.
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
description: >-
status command has been changed, the default human output now uses a list format
and summaries output. Full human output can be obtained with the new `human_full`
# Affected component; a word indicating the component this changeset affects.
component: agent

# PR URL; optional; the PR number that added the changeset.
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
# Please provide it if you are adding a fragment for a different PR.
#pr: https://github.com/owner/repo/1234

# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
# If not present is automatically filled by the tooling with the issue linked to the PR number.
#issue: https://github.com/owner/repo/1234
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
github.com/gorilla/mux v1.8.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901
github.com/josephspurrier/goversioninfo v0.0.0-20190209210621-63e6d1acd3dd
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
Expand Down Expand Up @@ -108,6 +109,7 @@ require (
github.com/markbates/pkger v0.17.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/iochan v1.0.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand All @@ -118,6 +120,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i
github.com/jcchavezs/porto v0.1.0 h1:Xmxxn25zQMmgE7/yHYmh19KcItG81hIwfbEEFnd6w/Q=
github.com/jcchavezs/porto v0.1.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A=
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw=
github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
Expand Down Expand Up @@ -890,6 +892,8 @@ github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
Expand Down Expand Up @@ -1043,6 +1047,7 @@ github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -1089,6 +1094,8 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down Expand Up @@ -1190,6 +1197,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
Expand Down Expand Up @@ -1631,6 +1639,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand Down
129 changes: 94 additions & 35 deletions internal/pkg/agent/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"fmt"
"io"
"os"
"text/tabwriter"
"sort"
"time"

"github.com/elastic/elastic-agent/pkg/control/v2/client"
Expand All @@ -19,16 +19,19 @@ import (

"github.com/spf13/cobra"

"github.com/jedib0t/go-pretty/v6/list"

"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
"github.com/elastic/elastic-agent/internal/pkg/cli"
)

type outputter func(io.Writer, interface{}) error

var statusOutputs = map[string]outputter{
"human": humanStateOutput,
"json": jsonOutput,
"yaml": yamlOutput,
"human": humanOutput,
"human_full": humanFullOutput,
"json": jsonOutput,
"yaml": yamlOutput,
}

func newStatusCommand(_ []string, streams *cli.IOStreams) *cobra.Command {
Expand All @@ -44,7 +47,7 @@ func newStatusCommand(_ []string, streams *cli.IOStreams) *cobra.Command {
},
}

cmd.Flags().String("output", "human", "Output the status information in either human, json, or yaml (default: human)")
cmd.Flags().String("output", "human", "Output the status information in either 'human', 'human_full', 'json', or 'yaml'. 'human' only shows non-healthy details, others show full details. (default: human)")

return cmd
}
Expand All @@ -69,6 +72,10 @@ func statusCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error
return fmt.Errorf("failed to communicate with Elastic Agent daemon: %w", err)
}

sort.SliceStable(state.Components, func(i, j int) bool { return state.Components[i].ID < state.Components[j].ID })
for _, c := range state.Components {
sort.SliceStable(c.Units, func(i, j int) bool { return c.Units[i].UnitID < c.Units[j].UnitID })
}
err = outputFunc(streams.Out, state)
if err != nil {
return err
Expand All @@ -82,43 +89,95 @@ func statusCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error
return nil
}

func humanStateOutput(w io.Writer, obj interface{}) error {
func formatStatus(state client.State, message string) string {
return fmt.Sprintf("status: (%s) %s", state, message)
}

func listComponentState(l list.Writer, components []client.ComponentState, all bool) {
for _, c := range components {
// see if any unit is not Healthy because component
// can be healthy with failed units
units_healthy := true
for _, u := range c.Units {
if u.State != client.Healthy {
units_healthy = false
break
}
}
if !all && units_healthy && (c.State == client.Healthy) {
continue
}
l.Indent()
l.AppendItem(c.ID)
l.Indent()
l.AppendItem(formatStatus(c.State, c.Message))
l.UnIndent()
for _, u := range c.Units {
if !all && (u.State == client.Healthy) {
continue
}
l.Indent()
l.AppendItem(u.UnitID)
l.Indent()
l.AppendItem(formatStatus(u.State, u.Message))
if all {
l.AppendItem(fmt.Sprintf("type: %s", u.UnitType))
}
l.UnIndent()
l.UnIndent()
}
l.UnIndent()
l.UnIndent()
}
}

func listAgentState(l list.Writer, state *client.AgentState, all bool) {
l.AppendItem("elastic-agent")
l.Indent()
l.AppendItem(formatStatus(state.State, state.Message))
if all {
l.AppendItem("info")
l.Indent()
l.AppendItem("id: " + state.Info.ID)
l.AppendItem("version: " + state.Info.Version)
l.AppendItem("commit: " + state.Info.Commit)
l.UnIndent()
}
l.UnIndent()
listComponentState(l, state.Components, all)
}

func listFleetState(l list.Writer, state *client.AgentState, all bool) {
l.AppendItem("fleet")
l.Indent()
l.AppendItem(formatStatus(state.FleetState, state.FleetMessage))
l.UnIndent()
}

func humanListOutput(w io.Writer, state *client.AgentState, all bool) error {
l := list.NewWriter()
l.SetStyle(list.StyleConnectedLight)
l.SetOutputMirror(w)
listFleetState(l, state, all)
listAgentState(l, state, all)
_ = l.Render()
return nil
}

func humanFullOutput(w io.Writer, obj interface{}) error {
status, ok := obj.(*client.AgentState)
if !ok {
return fmt.Errorf("unable to cast %T as *client.AgentStatus", obj)
}
return outputState(w, status)
return humanListOutput(w, status, true)
}

func outputState(w io.Writer, state *client.AgentState) error {
fmt.Fprintf(w, "State: %s\n", state.State)
if state.Message == "" {
fmt.Fprint(w, "Message: (no message)\n")
} else {
fmt.Fprintf(w, "Message: %s\n", state.Message)
}
fmt.Fprintf(w, "Fleet State: %s\n", state.FleetState)
if state.FleetMessage == "" {
fmt.Fprint(w, "Fleet Message: (no message)\n")
} else {
fmt.Fprintf(w, "Fleet Message: %s\n", state.FleetMessage)
}
if len(state.Components) == 0 {
fmt.Fprint(w, "Components: (none)\n")
} else {
fmt.Fprint(w, "Components:\n")
tw := tabwriter.NewWriter(w, 4, 1, 2, ' ', 0)
for _, comp := range state.Components {
fmt.Fprintf(tw, " * %s\t(%s)\n", comp.Name, comp.State)
if comp.Message == "" {
fmt.Fprint(tw, "\t(no message)\n")
} else {
fmt.Fprintf(tw, "\t%s\n", comp.Message)
}
}
tw.Flush()
func humanOutput(w io.Writer, obj interface{}) error {
status, ok := obj.(*client.AgentState)
if !ok {
return fmt.Errorf("unable to cast %T as *client.AgentStatus", obj)
}
return nil
return humanListOutput(w, status, false)
}

func jsonOutput(w io.Writer, out interface{}) error {
Expand Down
Loading

0 comments on commit 4cb6ef0

Please sign in to comment.