package main import ( "encoding/json" "io" "os" "sort" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/gosuri/uilive" "github.com/olekukonko/tablewriter" ) type outputWriter interface { WriteResult(res fleet.DistributedQueryResult) error } type resultOutput struct { HostIdentifier string `json:"host"` Rows []map[string]string `json:"rows"` Error *string `json:"error,omitempty"` } type jsonWriter struct { w io.Writer } func newJsonWriter(w io.Writer) *jsonWriter { if w == nil { w = os.Stdout } return &jsonWriter{w: w} } func (w *jsonWriter) WriteResult(res fleet.DistributedQueryResult) error { out := resultOutput{ HostIdentifier: res.Host.Hostname, Rows: res.Rows, Error: res.Error, } return json.NewEncoder(w.w).Encode(out) } type prettyWriter struct { results []fleet.DistributedQueryResult columns map[string]bool writer *uilive.Writer } func newPrettyWriter() *prettyWriter { return &prettyWriter{ columns: make(map[string]bool), writer: uilive.New(), } } func (w *prettyWriter) WriteResult(res fleet.DistributedQueryResult) error { w.results = append(w.results, res) // Recompute columns for _, row := range res.Rows { delete(row, "host_hostname") 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 { cols := []string{res.Host.Hostname} for _, col := range columns { cols = append(cols, row[col]) } table.Append(cols) } } table.Render() // Actually write the output w.writer.Flush() return nil }