2020-11-04 17:56:57 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2021-09-10 19:26:39 +00:00
|
|
|
"io"
|
2020-11-04 17:56:57 +00:00
|
|
|
"os"
|
2021-01-29 01:06:41 +00:00
|
|
|
"sort"
|
2020-11-04 17:56:57 +00:00
|
|
|
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2021-01-29 01:06:41 +00:00
|
|
|
"github.com/gosuri/uilive"
|
|
|
|
"github.com/olekukonko/tablewriter"
|
2020-11-04 17:56:57 +00:00
|
|
|
)
|
|
|
|
|
2021-01-29 01:06:41 +00:00
|
|
|
type outputWriter interface {
|
2021-06-06 22:07:29 +00:00
|
|
|
WriteResult(res fleet.DistributedQueryResult) error
|
2020-11-04 17:56:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type resultOutput struct {
|
|
|
|
HostIdentifier string `json:"host"`
|
|
|
|
Rows []map[string]string `json:"rows"`
|
2021-01-29 01:06:41 +00:00
|
|
|
Error *string `json:"error,omitempty"`
|
2020-11-04 17:56:57 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 19:26:39 +00:00
|
|
|
type jsonWriter struct {
|
|
|
|
w io.Writer
|
|
|
|
}
|
2020-11-04 17:56:57 +00:00
|
|
|
|
2021-09-10 19:26:39 +00:00
|
|
|
func newJsonWriter(w io.Writer) *jsonWriter {
|
|
|
|
if w == nil {
|
|
|
|
w = os.Stdout
|
|
|
|
}
|
|
|
|
return &jsonWriter{w: w}
|
2020-11-04 17:56:57 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (w *jsonWriter) WriteResult(res fleet.DistributedQueryResult) error {
|
2021-01-29 01:06:41 +00:00
|
|
|
out := resultOutput{
|
2021-06-24 00:32:19 +00:00
|
|
|
HostIdentifier: res.Host.Hostname,
|
2021-01-29 01:06:41 +00:00
|
|
|
Rows: res.Rows,
|
|
|
|
Error: res.Error,
|
|
|
|
}
|
2021-09-10 19:26:39 +00:00
|
|
|
return json.NewEncoder(w.w).Encode(out)
|
2020-11-04 17:56:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type prettyWriter struct {
|
2021-06-06 22:07:29 +00:00
|
|
|
results []fleet.DistributedQueryResult
|
2020-11-04 17:56:57 +00:00
|
|
|
columns map[string]bool
|
2021-01-29 01:06:41 +00:00
|
|
|
writer *uilive.Writer
|
2020-11-04 17:56:57 +00:00
|
|
|
}
|
|
|
|
|
2021-01-29 01:06:41 +00:00
|
|
|
func newPrettyWriter() *prettyWriter {
|
2020-11-04 17:56:57 +00:00
|
|
|
return &prettyWriter{
|
|
|
|
columns: make(map[string]bool),
|
2021-01-29 01:06:41 +00:00
|
|
|
writer: uilive.New(),
|
2020-11-04 17:56:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (w *prettyWriter) WriteResult(res fleet.DistributedQueryResult) error {
|
2020-11-04 17:56:57 +00:00
|
|
|
w.results = append(w.results, res)
|
|
|
|
|
|
|
|
// Recompute columns
|
|
|
|
for _, row := range res.Rows {
|
|
|
|
delete(row, "host_hostname")
|
2022-10-17 13:52:25 +00:00
|
|
|
delete(row, "host_display_name")
|
2020-11-04 17:56:57 +00:00
|
|
|
for col := range row {
|
|
|
|
w.columns[col] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
columns := []string{}
|
|
|
|
for col := range w.columns {
|
|
|
|
columns = append(columns, col)
|
|
|
|
}
|
|
|
|
sort.Strings(columns)
|
|
|
|
|
|
|
|
table := tablewriter.NewWriter(w.writer.Newline())
|
|
|
|
table.SetRowLine(true)
|
|
|
|
table.SetHeader(append([]string{"hostname"}, columns...))
|
|
|
|
|
|
|
|
// Extract columns from the results in the appropriate order
|
|
|
|
for _, res := range w.results {
|
|
|
|
for _, row := range res.Rows {
|
2021-06-24 00:32:19 +00:00
|
|
|
cols := []string{res.Host.Hostname}
|
2020-11-04 17:56:57 +00:00
|
|
|
for _, col := range columns {
|
|
|
|
cols = append(cols, row[col])
|
|
|
|
}
|
|
|
|
table.Append(cols)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
table.Render()
|
|
|
|
|
|
|
|
// Actually write the output
|
|
|
|
w.writer.Flush()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|