diff --git a/cmd/crowdsec-cli/alerts.go b/cmd/crowdsec-cli/alerts.go index 0fe9852519c..0bb310739d9 100644 --- a/cmd/crowdsec-cli/alerts.go +++ b/cmd/crowdsec-cli/alerts.go @@ -21,6 +21,7 @@ import ( "github.com/crowdsecurity/go-cs-lib/maptools" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" "github.com/crowdsecurity/crowdsec/pkg/apiclient" "github.com/crowdsecurity/crowdsec/pkg/cwversion" @@ -54,7 +55,8 @@ func DecisionsFromAlert(alert *models.Alert) string { } func (cli *cliAlerts) alertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error { - switch cli.cfg().Cscli.Output { + cfg := cli.cfg() + switch cfg.Cscli.Output { case "raw": csvwriter := csv.NewWriter(os.Stdout) header := []string{"id", "scope", "value", "reason", "country", "as", "decisions", "created_at"} @@ -104,7 +106,7 @@ func (cli *cliAlerts) alertsToTable(alerts *models.GetAlertsResponse, printMachi return nil } - alertsTable(color.Output, alerts, printMachine) + alertsTable(color.Output, cfg.Cscli.Color, alerts, printMachine) } return nil @@ -138,7 +140,9 @@ func (cli *cliAlerts) displayOneAlert(alert *models.Alert, withDetail bool) erro return err } - alertDecisionsTable(color.Output, alert) + cfg := cli.cfg() + + alertDecisionsTable(color.Output, cfg.Cscli.Color, alert) if len(alert.Meta) > 0 { fmt.Printf("\n - Context :\n") @@ -146,7 +150,7 @@ func (cli *cliAlerts) displayOneAlert(alert *models.Alert, withDetail bool) erro return alert.Meta[i].Key < alert.Meta[j].Key }) - table := newTable(color.Output) + table := cstable.New(color.Output, cfg.Cscli.Color) table.SetRowLines(false) table.SetHeaders("Key", "Value") @@ -171,7 +175,7 @@ func (cli *cliAlerts) displayOneAlert(alert *models.Alert, withDetail bool) erro fmt.Printf("\n - Events :\n") for _, event := range alert.Events { - alertEventTable(color.Output, event) + alertEventTable(color.Output, cfg.Cscli.Color, event) } } diff --git a/cmd/crowdsec-cli/alerts_table.go b/cmd/crowdsec-cli/alerts_table.go index fbde4d2aaa9..29383457ced 100644 --- a/cmd/crowdsec-cli/alerts_table.go +++ b/cmd/crowdsec-cli/alerts_table.go @@ -9,11 +9,12 @@ import ( log "github.com/sirupsen/logrus" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/pkg/models" ) -func alertsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachine bool) { - t := newTable(out) +func alertsTable(out io.Writer, wantColor string, alerts *models.GetAlertsResponse, printMachine bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) header := []string{"ID", "value", "reason", "country", "as", "decisions", "created_at"} @@ -51,9 +52,9 @@ func alertsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachine b t.Render() } -func alertDecisionsTable(out io.Writer, alert *models.Alert) { +func alertDecisionsTable(out io.Writer, wantColor string, alert *models.Alert) { foundActive := false - t := newTable(out) + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("ID", "scope:value", "action", "expiration", "created_at") @@ -90,10 +91,10 @@ func alertDecisionsTable(out io.Writer, alert *models.Alert) { } } -func alertEventTable(out io.Writer, event *models.Event) { +func alertEventTable(out io.Writer, wantColor string, event *models.Event) { fmt.Fprintf(out, "\n- Date: %s\n", *event.Timestamp) - t := newTable(out) + t := cstable.New(out, wantColor) t.SetHeaders("Key", "Value") sort.Slice(event.Meta, func(i, j int) bool { return event.Meta[i].Key < event.Meta[j].Key diff --git a/cmd/crowdsec-cli/bouncers.go b/cmd/crowdsec-cli/bouncers.go index 0673473d72a..d304b1b7867 100644 --- a/cmd/crowdsec-cli/bouncers.go +++ b/cmd/crowdsec-cli/bouncers.go @@ -17,6 +17,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1" "github.com/crowdsecurity/crowdsec/pkg/database" @@ -90,7 +91,7 @@ Note: This command requires database direct access, so is intended to be run on } func (cli *cliBouncers) listHuman(out io.Writer, bouncers ent.Bouncers) { - t := newLightTable(out).Writer + t := cstable.NewLight(out, cli.cfg().Cscli.Color).Writer t.AppendHeader(table.Row{"Name", "IP Address", "Valid", "Last API pull", "Type", "Version", "Auth Type"}) for _, b := range bouncers { @@ -411,7 +412,7 @@ cscli bouncers prune -d 45m --force`, } func (cli *cliBouncers) inspectHuman(out io.Writer, bouncer *ent.Bouncer) { - t := newTable(out).Writer + t := cstable.NewLight(out, cli.cfg().Cscli.Color).Writer t.SetTitle("Bouncer: " + bouncer.Name) diff --git a/cmd/crowdsec-cli/console.go b/cmd/crowdsec-cli/console.go index 3c7df395b30..979c9f0ea60 100644 --- a/cmd/crowdsec-cli/console.go +++ b/cmd/crowdsec-cli/console.go @@ -276,7 +276,7 @@ func (cli *cliConsole) newStatusCmd() *cobra.Command { consoleCfg := cfg.API.Server.ConsoleConfig switch cfg.Cscli.Output { case "human": - cmdConsoleStatusTable(color.Output, *consoleCfg) + cmdConsoleStatusTable(color.Output, cfg.Cscli.Color, *consoleCfg) case "json": out := map[string](*bool){ csconfig.SEND_MANUAL_SCENARIOS: consoleCfg.ShareManualDecisions, diff --git a/cmd/crowdsec-cli/console_table.go b/cmd/crowdsec-cli/console_table.go index 4623f3bb62a..94976618573 100644 --- a/cmd/crowdsec-cli/console_table.go +++ b/cmd/crowdsec-cli/console_table.go @@ -3,17 +3,19 @@ package main import ( "io" - "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/table" + "github.com/jedib0t/go-pretty/v6/text" + + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/emoji" ) -func cmdConsoleStatusTable(out io.Writer, consoleCfg csconfig.ConsoleConfig) { - t := newTable(out) +func cmdConsoleStatusTable(out io.Writer, wantColor string, consoleCfg csconfig.ConsoleConfig) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Option Name", "Activated", "Description") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) for _, option := range csconfig.CONSOLE_CONFIGS { activated := emoji.CrossMark diff --git a/cmd/crowdsec-cli/prettytable.go b/cmd/crowdsec-cli/cstable/cstable.go similarity index 77% rename from cmd/crowdsec-cli/prettytable.go rename to cmd/crowdsec-cli/cstable/cstable.go index f17472722f1..f7ddb604d84 100644 --- a/cmd/crowdsec-cli/prettytable.go +++ b/cmd/crowdsec-cli/cstable/cstable.go @@ -1,4 +1,4 @@ -package main +package cstable // transisional file to keep (minimal) backwards compatibility with the old table // we can migrate the code to the new dependency later, it can already use the Writer interface @@ -6,11 +6,36 @@ package main import ( "fmt" "io" + "os" "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" + isatty "github.com/mattn/go-isatty" ) +func RenderTitle(out io.Writer, title string) { + if out == nil { + panic("renderTableTitle: out is nil") + } + + if title == "" { + return + } + + fmt.Fprintln(out, title) +} + +func shouldWeColorize(wantColor string) bool { + switch wantColor { + case "yes": + return true + case "no": + return false + default: + return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) + } +} + type Table struct { Writer table.Writer output io.Writer @@ -18,7 +43,7 @@ type Table struct { alignHeader []text.Align } -func newTable(out io.Writer) *Table { +func New(out io.Writer, wantColor string) *Table { if out == nil { panic("newTable: out is nil") } @@ -26,14 +51,14 @@ func newTable(out io.Writer) *Table { t := table.NewWriter() // colorize output, use unicode box characters - fancy := shouldWeColorize() + fancy := shouldWeColorize(wantColor) - color := table.ColorOptions{} + colorOptions := table.ColorOptions{} if fancy { - color.Header = text.Colors{text.Italic} - color.Border = text.Colors{text.FgHiBlack} - color.Separator = text.Colors{text.FgHiBlack} + colorOptions.Header = text.Colors{text.Italic} + colorOptions.Border = text.Colors{text.FgHiBlack} + colorOptions.Separator = text.Colors{text.FgHiBlack} } // no upper/lower case transformations @@ -46,7 +71,7 @@ func newTable(out io.Writer) *Table { style := table.Style{ Box: box, - Color: color, + Color: colorOptions, Format: format, HTML: table.DefaultHTMLOptions, Options: table.OptionsDefault, @@ -63,8 +88,8 @@ func newTable(out io.Writer) *Table { } } -func newLightTable(output io.Writer) *Table { - t := newTable(output) +func NewLight(output io.Writer, wantColor string) *Table { + t := New(output, wantColor) s := t.Writer.Style() s.Box.Left = "" s.Box.LeftSeparator = "" @@ -100,6 +125,7 @@ func (t *Table) setColumnConfigs() { WidthMaxEnforcer: text.WrapSoft, }) } + t.Writer.SetColumnConfigs(configs) } diff --git a/cmd/crowdsec-cli/decisions_table.go b/cmd/crowdsec-cli/decisions_table.go index 10021e4dd4b..02952f93b85 100644 --- a/cmd/crowdsec-cli/decisions_table.go +++ b/cmd/crowdsec-cli/decisions_table.go @@ -5,11 +5,12 @@ import ( "io" "strconv" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/pkg/models" ) func (cli *cliDecisions) decisionsTable(out io.Writer, alerts *models.GetAlertsResponse, printMachine bool) { - t := newTable(out) + t := cstable.New(out, cli.cfg().Cscli.Color) t.SetRowLines(false) header := []string{"ID", "Source", "Scope:Value", "Reason", "Action", "Country", "AS", "Events", "expiration", "Alert ID"} diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go index 737b93d8da8..7e00eb64b33 100644 --- a/cmd/crowdsec-cli/hub.go +++ b/cmd/crowdsec-cli/hub.go @@ -72,7 +72,7 @@ func (cli *cliHub) list(all bool) error { } } - err = listItems(color.Output, cwhub.ItemTypes, items, true, cfg.Cscli.Output) + err = listItems(color.Output, cfg.Cscli.Color, cwhub.ItemTypes, items, true, cfg.Cscli.Output) if err != nil { return err } diff --git a/cmd/crowdsec-cli/hubtest.go b/cmd/crowdsec-cli/hubtest.go index 8796fa48a17..2a4635d39f1 100644 --- a/cmd/crowdsec-cli/hubtest.go +++ b/cmd/crowdsec-cli/hubtest.go @@ -371,7 +371,7 @@ func (cli *cliHubTest) NewRunCmd() *cobra.Command { switch cfg.Cscli.Output { case "human": - hubTestResultTable(color.Output, testResult) + hubTestResultTable(color.Output, cfg.Cscli.Color, testResult) case "json": jsonResult := make(map[string][]string, 0) jsonResult["success"] = make([]string, 0) @@ -480,7 +480,7 @@ func (cli *cliHubTest) NewListCmd() *cobra.Command { switch cfg.Cscli.Output { case "human": - hubTestListTable(color.Output, hubPtr.Tests) + hubTestListTable(color.Output, cfg.Cscli.Color, hubPtr.Tests) case "json": j, err := json.MarshalIndent(hubPtr.Tests, " ", " ") if err != nil { @@ -505,7 +505,9 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp if err := HubTest.LoadAllTests(); err != nil { return fmt.Errorf("unable to load all tests: %+v", err) } + var err error + scenarioCoverage := []hubtest.Coverage{} parserCoverage := []hubtest.Coverage{} appsecRuleCoverage := []hubtest.Coverage{} @@ -521,12 +523,15 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp if err != nil { return fmt.Errorf("while getting parser coverage: %w", err) } + parserTested := 0 + for _, test := range parserCoverage { if test.TestsCount > 0 { parserTested++ } } + parserCoveragePercent = int(math.Round((float64(parserTested) / float64(len(parserCoverage)) * 100))) } @@ -537,6 +542,7 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp } scenarioTested := 0 + for _, test := range scenarioCoverage { if test.TestsCount > 0 { scenarioTested++ @@ -553,11 +559,13 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp } appsecRuleTested := 0 + for _, test := range appsecRuleCoverage { if test.TestsCount > 0 { appsecRuleTested++ } } + appsecRuleCoveragePercent = int(math.Round((float64(appsecRuleTested) / float64(len(appsecRuleCoverage)) * 100))) } @@ -572,30 +580,34 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp case showAppsecCov: fmt.Printf("appsec_rules=%d%%", appsecRuleCoveragePercent) } + return nil } switch cfg.Cscli.Output { case "human": if showParserCov || showAll { - hubTestParserCoverageTable(color.Output, parserCoverage) + hubTestParserCoverageTable(color.Output, cfg.Cscli.Color, parserCoverage) } if showScenarioCov || showAll { - hubTestScenarioCoverageTable(color.Output, scenarioCoverage) + hubTestScenarioCoverageTable(color.Output, cfg.Cscli.Color, scenarioCoverage) } if showAppsecCov || showAll { - hubTestAppsecRuleCoverageTable(color.Output, appsecRuleCoverage) + hubTestAppsecRuleCoverageTable(color.Output, cfg.Cscli.Color, appsecRuleCoverage) } fmt.Println() + if showParserCov || showAll { fmt.Printf("PARSERS : %d%% of coverage\n", parserCoveragePercent) } + if showScenarioCov || showAll { fmt.Printf("SCENARIOS : %d%% of coverage\n", scenarioCoveragePercent) } + if showAppsecCov || showAll { fmt.Printf("APPSEC RULES : %d%% of coverage\n", appsecRuleCoveragePercent) } @@ -604,16 +616,21 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp if err != nil { return err } + fmt.Printf("%s", dump) + dump, err = json.MarshalIndent(scenarioCoverage, "", " ") if err != nil { return err } + fmt.Printf("%s", dump) + dump, err = json.MarshalIndent(appsecRuleCoverage, "", " ") if err != nil { return err } + fmt.Printf("%s", dump) default: return errors.New("only human/json output modes are supported") diff --git a/cmd/crowdsec-cli/hubtest_table.go b/cmd/crowdsec-cli/hubtest_table.go index 1b76e75263e..1fa0f990be2 100644 --- a/cmd/crowdsec-cli/hubtest_table.go +++ b/cmd/crowdsec-cli/hubtest_table.go @@ -4,16 +4,18 @@ import ( "fmt" "io" - "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/table" + "github.com/jedib0t/go-pretty/v6/text" + + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/pkg/emoji" "github.com/crowdsecurity/crowdsec/pkg/hubtest" ) -func hubTestResultTable(out io.Writer, testResult map[string]bool) { - t := newLightTable(out) +func hubTestResultTable(out io.Writer, wantColor string, testResult map[string]bool) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Test", "Result") - t.SetHeaderAlignment(table.AlignLeft) - t.SetAlignment(table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft) + t.SetAlignment(text.AlignLeft) for testName, success := range testResult { status := emoji.CheckMarkButton @@ -27,11 +29,11 @@ func hubTestResultTable(out io.Writer, testResult map[string]bool) { t.Render() } -func hubTestListTable(out io.Writer, tests []*hubtest.HubTestItem) { - t := newLightTable(out) +func hubTestListTable(out io.Writer, wantColor string, tests []*hubtest.HubTestItem) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Name", "Path") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft) - t.SetAlignment(table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft) for _, test := range tests { t.AddRow(test.Name, test.Path) @@ -40,11 +42,11 @@ func hubTestListTable(out io.Writer, tests []*hubtest.HubTestItem) { t.Render() } -func hubTestParserCoverageTable(out io.Writer, coverage []hubtest.Coverage) { - t := newLightTable(out) +func hubTestParserCoverageTable(out io.Writer, wantColor string, coverage []hubtest.Coverage) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Parser", "Status", "Number of tests") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) parserTested := 0 @@ -61,11 +63,11 @@ func hubTestParserCoverageTable(out io.Writer, coverage []hubtest.Coverage) { t.Render() } -func hubTestAppsecRuleCoverageTable(out io.Writer, coverage []hubtest.Coverage) { - t := newLightTable(out) +func hubTestAppsecRuleCoverageTable(out io.Writer, wantColor string, coverage []hubtest.Coverage) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Appsec Rule", "Status", "Number of tests") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) parserTested := 0 @@ -82,11 +84,11 @@ func hubTestAppsecRuleCoverageTable(out io.Writer, coverage []hubtest.Coverage) t.Render() } -func hubTestScenarioCoverageTable(out io.Writer, coverage []hubtest.Coverage) { - t := newLightTable(out) +func hubTestScenarioCoverageTable(out io.Writer, wantColor string, coverage []hubtest.Coverage) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Scenario", "Status", "Number of tests") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) parserTested := 0 diff --git a/cmd/crowdsec-cli/item_metrics.go b/cmd/crowdsec-cli/item_metrics.go index 9459968790b..b4b8c3c26b5 100644 --- a/cmd/crowdsec-cli/item_metrics.go +++ b/cmd/crowdsec-cli/item_metrics.go @@ -18,23 +18,23 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func ShowMetrics(prometheusURL string, hubItem *cwhub.Item) error { +func ShowMetrics(prometheusURL string, hubItem *cwhub.Item, wantColor string) error { switch hubItem.Type { case cwhub.PARSERS: metrics := GetParserMetric(prometheusURL, hubItem.Name) - parserMetricsTable(color.Output, hubItem.Name, metrics) + parserMetricsTable(color.Output, wantColor, hubItem.Name, metrics) case cwhub.SCENARIOS: metrics := GetScenarioMetric(prometheusURL, hubItem.Name) - scenarioMetricsTable(color.Output, hubItem.Name, metrics) + scenarioMetricsTable(color.Output, wantColor, hubItem.Name, metrics) case cwhub.COLLECTIONS: for _, sub := range hubItem.SubItems() { - if err := ShowMetrics(prometheusURL, sub); err != nil { + if err := ShowMetrics(prometheusURL, sub, wantColor); err != nil { return err } } case cwhub.APPSEC_RULES: metrics := GetAppsecRuleMetric(prometheusURL, hubItem.Name) - appsecMetricsTable(color.Output, hubItem.Name, metrics) + appsecMetricsTable(color.Output, wantColor, hubItem.Name, metrics) default: // no metrics for this item type } diff --git a/cmd/crowdsec-cli/itemcli.go b/cmd/crowdsec-cli/itemcli.go index 55396a10995..64c18ae89b1 100644 --- a/cmd/crowdsec-cli/itemcli.go +++ b/cmd/crowdsec-cli/itemcli.go @@ -381,7 +381,7 @@ func (cli cliItem) inspect(ctx context.Context, args []string, url string, diff continue } - if err = inspectItem(item, !noMetrics, cfg.Cscli.Output, cfg.Cscli.PrometheusUrl); err != nil { + if err = inspectItem(item, !noMetrics, cfg.Cscli.Output, cfg.Cscli.PrometheusUrl, cfg.Cscli.Color); err != nil { return err } @@ -442,7 +442,7 @@ func (cli cliItem) list(args []string, all bool) error { return err } - return listItems(color.Output, []string{cli.name}, items, false, cfg.Cscli.Output) + return listItems(color.Output, cfg.Cscli.Color, []string{cli.name}, items, false, cfg.Cscli.Output) } func (cli cliItem) newListCmd() *cobra.Command { diff --git a/cmd/crowdsec-cli/items.go b/cmd/crowdsec-cli/items.go index 9af432c32c1..b0c03922166 100644 --- a/cmd/crowdsec-cli/items.go +++ b/cmd/crowdsec-cli/items.go @@ -54,7 +54,7 @@ func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly b return items, nil } -func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item, omitIfEmpty bool, output string) error { +func listItems(out io.Writer, wantColor string, itemTypes []string, items map[string][]*cwhub.Item, omitIfEmpty bool, output string) error { switch output { case "human": nothingToDisplay := true @@ -64,7 +64,7 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item continue } - listHubItemTable(out, "\n"+strings.ToUpper(itemType), items[itemType]) + listHubItemTable(out, wantColor, "\n"+strings.ToUpper(itemType), items[itemType]) nothingToDisplay = false } @@ -143,7 +143,7 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item return nil } -func inspectItem(item *cwhub.Item, showMetrics bool, output string, prometheusURL string) error { +func inspectItem(item *cwhub.Item, showMetrics bool, output string, prometheusURL string, wantColor string) error { switch output { case "human", "raw": enc := yaml.NewEncoder(os.Stdout) @@ -174,7 +174,7 @@ func inspectItem(item *cwhub.Item, showMetrics bool, output string, prometheusUR if showMetrics { fmt.Printf("\nCurrent metrics: \n") - if err := ShowMetrics(prometheusURL, item); err != nil { + if err := ShowMetrics(prometheusURL, item, wantColor); err != nil { return err } } diff --git a/cmd/crowdsec-cli/machines.go b/cmd/crowdsec-cli/machines.go index 8796d3de9b8..2da5e20229c 100644 --- a/cmd/crowdsec-cli/machines.go +++ b/cmd/crowdsec-cli/machines.go @@ -24,6 +24,7 @@ import ( "github.com/crowdsecurity/machineid" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" "github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/cwhub" @@ -156,7 +157,7 @@ Note: This command requires database direct access, so is intended to be run on return cmd } -func (*cliMachines) inspectHubHuman(out io.Writer, machine *ent.Machine) { +func (cli *cliMachines) inspectHubHuman(out io.Writer, machine *ent.Machine) { state := machine.Hubstate if len(state) == 0 { @@ -179,7 +180,7 @@ func (*cliMachines) inspectHubHuman(out io.Writer, machine *ent.Machine) { } for itemType, rows := range rowsByType { - t := newTable(out).Writer + t := cstable.New(out, cli.cfg().Cscli.Color).Writer t.AppendHeader(table.Row{"Name", "Status", "Version"}) t.SetTitle(itemType) t.AppendRows(rows) @@ -188,7 +189,7 @@ func (*cliMachines) inspectHubHuman(out io.Writer, machine *ent.Machine) { } func (cli *cliMachines) listHuman(out io.Writer, machines ent.Machines) { - t := newLightTable(out).Writer + t := cstable.NewLight(out, cli.cfg().Cscli.Color).Writer t.AppendHeader(table.Row{"Name", "IP Address", "Last Update", "Status", "Version", "OS", "Auth Type", "Last Heartbeat"}) for _, m := range machines { @@ -625,8 +626,8 @@ func (cli *cliMachines) newValidateCmd() *cobra.Command { return cmd } -func (*cliMachines) inspectHuman(out io.Writer, machine *ent.Machine) { - t := newTable(out).Writer +func (cli *cliMachines) inspectHuman(out io.Writer, machine *ent.Machine) { + t := cstable.New(out, cli.cfg().Cscli.Color).Writer t.SetTitle("Machine: " + machine.MachineId) diff --git a/cmd/crowdsec-cli/metrics.go b/cmd/crowdsec-cli/metrics.go index 7858a7a7b2b..a9457bb849e 100644 --- a/cmd/crowdsec-cli/metrics.go +++ b/cmd/crowdsec-cli/metrics.go @@ -19,6 +19,8 @@ import ( "github.com/crowdsecurity/go-cs-lib/maptools" "github.com/crowdsecurity/go-cs-lib/trace" + + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" ) type ( @@ -49,7 +51,7 @@ var ( ) type metricSection interface { - Table(out io.Writer, noUnit bool, showEmpty bool) + Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) Description() (string, string) } @@ -263,7 +265,7 @@ func NewCLIMetrics(cfg configGetter) *cliMetrics { } } -func (ms metricStore) Format(out io.Writer, sections []string, formatType string, noUnit bool) error { +func (ms metricStore) Format(out io.Writer, wantColor string, sections []string, formatType string, noUnit bool) error { // copy only the sections we want want := map[string]metricSection{} @@ -282,7 +284,7 @@ func (ms metricStore) Format(out io.Writer, sections []string, formatType string switch formatType { case "human": for _, section := range maptools.SortedKeys(want) { - want[section].Table(out, noUnit, showEmpty) + want[section].Table(out, wantColor, noUnit, showEmpty) } case "json": x, err := json.MarshalIndent(want, "", " ") @@ -331,7 +333,7 @@ func (cli *cliMetrics) show(sections []string, url string, noUnit bool) error { } } - return ms.Format(color.Output, sections, cfg.Cscli.Output, noUnit) + return ms.Format(color.Output, cfg.Cscli.Color, sections, cfg.Cscli.Output, noUnit) } func (cli *cliMetrics) NewCommand() *cobra.Command { @@ -449,7 +451,7 @@ func (cli *cliMetrics) list() error { switch cli.cfg().Cscli.Output { case "human": - t := newTable(color.Output) + t := cstable.New(color.Output, cli.cfg().Cscli.Color) t.SetRowLines(true) t.SetHeaders("Type", "Title", "Description") diff --git a/cmd/crowdsec-cli/metrics_table.go b/cmd/crowdsec-cli/metrics_table.go index f42d5bdaf91..38a4d3bbcc4 100644 --- a/cmd/crowdsec-cli/metrics_table.go +++ b/cmd/crowdsec-cli/metrics_table.go @@ -7,17 +7,18 @@ import ( "sort" "strconv" + "github.com/jedib0t/go-pretty/v6/text" log "github.com/sirupsen/logrus" "github.com/crowdsecurity/go-cs-lib/maptools" - "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/table" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" ) // ErrNilTable means a nil pointer was passed instead of a table instance. This is a programming error. var ErrNilTable = errors.New("nil table") -func lapiMetricsToTable(t *Table, stats map[string]map[string]map[string]int) int { +func lapiMetricsToTable(t *cstable.Table, stats map[string]map[string]map[string]int) int { // stats: machine -> route -> method -> count // sort keys to keep consistent order when printing machineKeys := []string{} @@ -55,7 +56,7 @@ func lapiMetricsToTable(t *Table, stats map[string]map[string]map[string]int) in return numRows } -func wlMetricsToTable(t *Table, stats map[string]map[string]map[string]int, noUnit bool) (int, error) { +func wlMetricsToTable(t *cstable.Table, stats map[string]map[string]map[string]int, noUnit bool) (int, error) { if t == nil { return 0, ErrNilTable } @@ -93,7 +94,7 @@ func wlMetricsToTable(t *Table, stats map[string]map[string]map[string]int, noUn return numRows, nil } -func metricsToTable(t *Table, stats map[string]map[string]int, keys []string, noUnit bool) (int, error) { +func metricsToTable(t *cstable.Table, stats map[string]map[string]int, keys []string, noUnit bool) (int, error) { if t == nil { return 0, ErrNilTable } @@ -145,11 +146,11 @@ func (s statBucket) Process(bucket, metric string, val int) { s[bucket][metric] += val } -func (s statBucket) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statBucket) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Scenario", "Current Count", "Overflows", "Instantiated", "Poured", "Expired") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) keys := []string{"curr_count", "overflow", "instantiation", "pour", "underflow"} @@ -157,7 +158,7 @@ func (s statBucket) Table(out io.Writer, noUnit bool, showEmpty bool) { log.Warningf("while collecting scenario stats: %s", err) } else if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -178,11 +179,11 @@ func (s statAcquis) Process(source, metric string, val int) { s[source][metric] += val } -func (s statAcquis) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statAcquis) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Source", "Lines read", "Lines parsed", "Lines unparsed", "Lines poured to bucket", "Lines whitelisted") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) keys := []string{"reads", "parsed", "unparsed", "pour", "whitelisted"} @@ -190,7 +191,7 @@ func (s statAcquis) Table(out io.Writer, noUnit bool, showEmpty bool) { log.Warningf("while collecting acquis stats: %s", err) } else if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -208,11 +209,11 @@ func (s statAppsecEngine) Process(appsecEngine, metric string, val int) { s[appsecEngine][metric] += val } -func (s statAppsecEngine) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statAppsecEngine) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Appsec Engine", "Processed", "Blocked") - t.SetAlignment(table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft) keys := []string{"processed", "blocked"} @@ -220,7 +221,7 @@ func (s statAppsecEngine) Table(out io.Writer, noUnit bool, showEmpty bool) { log.Warningf("while collecting appsec stats: %s", err) } else if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -242,19 +243,19 @@ func (s statAppsecRule) Process(appsecEngine, appsecRule string, metric string, s[appsecEngine][appsecRule][metric] += val } -func (s statAppsecRule) Table(out io.Writer, noUnit bool, showEmpty bool) { +func (s statAppsecRule) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { for appsecEngine, appsecEngineRulesStats := range s { - t := newTable(out) + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Rule ID", "Triggered") - t.SetAlignment(table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft) keys := []string{"triggered"} if numRows, err := metricsToTable(t, appsecEngineRulesStats, keys, noUnit); err != nil { log.Warningf("while collecting appsec rules stats: %s", err) } else if numRows > 0 || showEmpty { - renderTableTitle(out, fmt.Sprintf("\nAppsec '%s' Rules Metrics:", appsecEngine)) + cstable.RenderTitle(out, fmt.Sprintf("\nAppsec '%s' Rules Metrics:", appsecEngine)) t.Render() } } @@ -277,17 +278,17 @@ func (s statWhitelist) Process(whitelist, reason, metric string, val int) { s[whitelist][reason][metric] += val } -func (s statWhitelist) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statWhitelist) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Whitelist", "Reason", "Hits", "Whitelisted") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) if numRows, err := wlMetricsToTable(t, s, noUnit); err != nil { log.Warningf("while collecting parsers stats: %s", err) } else if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -307,11 +308,11 @@ func (s statParser) Process(parser, metric string, val int) { s[parser][metric] += val } -func (s statParser) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statParser) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Parsers", "Hits", "Parsed", "Unparsed") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) keys := []string{"hits", "parsed", "unparsed"} @@ -319,7 +320,7 @@ func (s statParser) Table(out io.Writer, noUnit bool, showEmpty bool) { log.Warningf("while collecting parsers stats: %s", err) } else if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -339,11 +340,11 @@ func (s statStash) Process(name, mtype string, val int) { } } -func (s statStash) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statStash) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Name", "Type", "Items") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) // unfortunately, we can't reuse metricsToTable as the structure is too different :/ numRows := 0 @@ -363,7 +364,7 @@ func (s statStash) Table(out io.Writer, noUnit bool, showEmpty bool) { if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -381,11 +382,11 @@ func (s statLapi) Process(route, method string, val int) { s[route][method] += val } -func (s statLapi) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statLapi) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Route", "Method", "Hits") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) // unfortunately, we can't reuse metricsToTable as the structure is too different :/ numRows := 0 @@ -415,7 +416,7 @@ func (s statLapi) Table(out io.Writer, noUnit bool, showEmpty bool) { if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -437,17 +438,17 @@ func (s statLapiMachine) Process(machine, route, method string, val int) { s[machine][route][method] += val } -func (s statLapiMachine) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statLapiMachine) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Machine", "Route", "Method", "Hits") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) numRows := lapiMetricsToTable(t, s) if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -469,17 +470,17 @@ func (s statLapiBouncer) Process(bouncer, route, method string, val int) { s[bouncer][route][method] += val } -func (s statLapiBouncer) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statLapiBouncer) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Bouncer", "Route", "Method", "Hits") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) numRows := lapiMetricsToTable(t, s) if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -509,11 +510,11 @@ func (s statLapiDecision) Process(bouncer, fam string, val int) { s[bouncer] = x } -func (s statLapiDecision) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statLapiDecision) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Bouncer", "Empty answers", "Non-empty answers") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft) numRows := 0 @@ -529,7 +530,7 @@ func (s statLapiDecision) Table(out io.Writer, noUnit bool, showEmpty bool) { if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -552,11 +553,11 @@ func (s statDecision) Process(reason, origin, action string, val int) { s[reason][origin][action] += val } -func (s statDecision) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statDecision) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Reason", "Origin", "Action", "Count") - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) numRows := 0 @@ -577,7 +578,7 @@ func (s statDecision) Table(out io.Writer, noUnit bool, showEmpty bool) { if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } @@ -591,11 +592,11 @@ func (s statAlert) Process(reason string, val int) { s[reason] += val } -func (s statAlert) Table(out io.Writer, noUnit bool, showEmpty bool) { - t := newTable(out) +func (s statAlert) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) { + t := cstable.New(out, wantColor) t.SetRowLines(false) t.SetHeaders("Reason", "Count") - t.SetAlignment(table.AlignLeft, table.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft) numRows := 0 @@ -610,7 +611,7 @@ func (s statAlert) Table(out io.Writer, noUnit bool, showEmpty bool) { if numRows > 0 || showEmpty { title, _ := s.Description() - renderTableTitle(out, "\n"+title+":") + cstable.RenderTitle(out, "\n"+title+":") t.Render() } } diff --git a/cmd/crowdsec-cli/notifications.go b/cmd/crowdsec-cli/notifications.go index 768d6a1c47e..4d757fca44e 100644 --- a/cmd/crowdsec-cli/notifications.go +++ b/cmd/crowdsec-cli/notifications.go @@ -167,7 +167,7 @@ func (cli *cliNotifications) NewListCmd() *cobra.Command { } if cfg.Cscli.Output == "human" { - notificationListTable(color.Output, ncfgs) + notificationListTable(color.Output, cfg.Cscli.Color, ncfgs) } else if cfg.Cscli.Output == "json" { x, err := json.MarshalIndent(ncfgs, "", " ") if err != nil { diff --git a/cmd/crowdsec-cli/notifications_table.go b/cmd/crowdsec-cli/notifications_table.go index b96c8ca4783..2976797bd8a 100644 --- a/cmd/crowdsec-cli/notifications_table.go +++ b/cmd/crowdsec-cli/notifications_table.go @@ -5,15 +5,17 @@ import ( "sort" "strings" - "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/table" + "github.com/jedib0t/go-pretty/v6/text" + + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/pkg/emoji" ) -func notificationListTable(out io.Writer, ncfgs map[string]NotificationsCfg) { - t := newLightTable(out) +func notificationListTable(out io.Writer, wantColor string, ncfgs map[string]NotificationsCfg) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Active", "Name", "Type", "Profile name") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) keys := make([]string, 0, len(ncfgs)) for k := range ncfgs { diff --git a/cmd/crowdsec-cli/support.go b/cmd/crowdsec-cli/support.go index 1f98768f778..b026d31412e 100644 --- a/cmd/crowdsec-cli/support.go +++ b/cmd/crowdsec-cli/support.go @@ -94,7 +94,7 @@ func (cli *cliSupport) dumpMetrics(ctx context.Context, zw *zip.Writer) error { return err } - if err := ms.Format(humanMetrics, nil, "human", false); err != nil { + if err := ms.Format(humanMetrics, cfg.Cscli.Color, nil, "human", false); err != nil { return fmt.Errorf("could not format prometheus metrics: %w", err) } @@ -173,7 +173,7 @@ func (cli *cliSupport) dumpHubItems(zw *zip.Writer, hub *cwhub.Hub, itemType str return fmt.Errorf("could not collect %s list: %w", itemType, err) } - if err := listItems(out, []string{itemType}, items, false, "human"); err != nil { + if err := listItems(out, cli.cfg().Cscli.Color, []string{itemType}, items, false, "human"); err != nil { return fmt.Errorf("could not list %s: %w", itemType, err) } diff --git a/cmd/crowdsec-cli/table/align.go b/cmd/crowdsec-cli/table/align.go deleted file mode 100644 index e0582007c57..00000000000 --- a/cmd/crowdsec-cli/table/align.go +++ /dev/null @@ -1,12 +0,0 @@ -package table - -import ( - "github.com/jedib0t/go-pretty/v6/text" -) - -// temporary, backward compatibility only - -const ( - AlignLeft = text.AlignLeft - AlignRight = text.AlignRight -) diff --git a/cmd/crowdsec-cli/tables.go b/cmd/crowdsec-cli/tables.go deleted file mode 100644 index e6dba0c2644..00000000000 --- a/cmd/crowdsec-cli/tables.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - - isatty "github.com/mattn/go-isatty" -) - -func shouldWeColorize() bool { - switch csConfig.Cscli.Color { - case "yes": - return true - case "no": - return false - default: - return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) - } -} - -func renderTableTitle(out io.Writer, title string) { - if out == nil { - panic("renderTableTitle: out is nil") - } - - if title == "" { - return - } - - fmt.Fprintln(out, title) -} diff --git a/cmd/crowdsec-cli/utils_table.go b/cmd/crowdsec-cli/utils_table.go index d7d26a65c12..c0043e47ee3 100644 --- a/cmd/crowdsec-cli/utils_table.go +++ b/cmd/crowdsec-cli/utils_table.go @@ -5,28 +5,30 @@ import ( "io" "strconv" - "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/table" + "github.com/jedib0t/go-pretty/v6/text" + + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/pkg/cwhub" "github.com/crowdsecurity/crowdsec/pkg/emoji" ) -func listHubItemTable(out io.Writer, title string, items []*cwhub.Item) { - t := newLightTable(out) +func listHubItemTable(out io.Writer, wantColor string, title string, items []*cwhub.Item) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Name", fmt.Sprintf("%v Status", emoji.Package), "Version", "Local Path") - t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) - t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) + t.SetHeaderAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) + t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft) for _, item := range items { status := fmt.Sprintf("%v %s", item.State.Emoji(), item.State.Text()) t.AddRow(item.Name, status, item.State.LocalVersion, item.State.LocalPath) } - renderTableTitle(out, title) + cstable.RenderTitle(out, title) t.Render() } -func appsecMetricsTable(out io.Writer, itemName string, metrics map[string]int) { - t := newTable(out) +func appsecMetricsTable(out io.Writer, wantColor string, itemName string, metrics map[string]int) { + t := cstable.NewLight(out, wantColor) t.SetHeaders("Inband Hits", "Outband Hits") t.AddRow( @@ -34,16 +36,16 @@ func appsecMetricsTable(out io.Writer, itemName string, metrics map[string]int) strconv.Itoa(metrics["outband_hits"]), ) - renderTableTitle(out, fmt.Sprintf("\n - (AppSec Rule) %s:", itemName)) + cstable.RenderTitle(out, fmt.Sprintf("\n - (AppSec Rule) %s:", itemName)) t.Render() } -func scenarioMetricsTable(out io.Writer, itemName string, metrics map[string]int) { +func scenarioMetricsTable(out io.Writer, wantColor string, itemName string, metrics map[string]int) { if metrics["instantiation"] == 0 { return } - t := newTable(out) + t := cstable.New(out, wantColor) t.SetHeaders("Current Count", "Overflows", "Instantiated", "Poured", "Expired") t.AddRow( @@ -54,12 +56,12 @@ func scenarioMetricsTable(out io.Writer, itemName string, metrics map[string]int strconv.Itoa(metrics["underflow"]), ) - renderTableTitle(out, fmt.Sprintf("\n - (Scenario) %s:", itemName)) + cstable.RenderTitle(out, fmt.Sprintf("\n - (Scenario) %s:", itemName)) t.Render() } -func parserMetricsTable(out io.Writer, itemName string, metrics map[string]map[string]int) { - t := newTable(out) +func parserMetricsTable(out io.Writer, wantColor string, itemName string, metrics map[string]map[string]int) { + t := cstable.New(out, wantColor) t.SetHeaders("Parsers", "Hits", "Parsed", "Unparsed") // don't show table if no hits @@ -79,7 +81,7 @@ func parserMetricsTable(out io.Writer, itemName string, metrics map[string]map[s } if showTable { - renderTableTitle(out, fmt.Sprintf("\n - (Parser) %s:", itemName)) + cstable.RenderTitle(out, fmt.Sprintf("\n - (Parser) %s:", itemName)) t.Render() } }