Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

command cscli [machines|bouncers] inspect #3103

Merged
merged 2 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ linters-settings:
disabled: true
- name: struct-tag
disabled: true
- name: redundant-import-alias
disabled: true
- name: time-equal
disabled: true
- name: var-naming
Expand Down
217 changes: 183 additions & 34 deletions cmd/crowdsec-cli/bouncers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@
"encoding/json"
"errors"
"fmt"
"io"
"os"
"slices"
"strings"
"time"

"github.com/AlecAivazis/survey/v2"
"github.com/fatih/color"
"github.com/jedib0t/go-pretty/v6/table"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
"github.com/crowdsecurity/crowdsec/pkg/database"
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/bouncer"
"github.com/crowdsecurity/crowdsec/pkg/emoji"
"github.com/crowdsecurity/crowdsec/pkg/types"
)

Expand Down Expand Up @@ -79,54 +84,116 @@
cmd.AddCommand(cli.newAddCmd())
cmd.AddCommand(cli.newDeleteCmd())
cmd.AddCommand(cli.newPruneCmd())
cmd.AddCommand(cli.newInspectCmd())

Check warning on line 87 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L87

Added line #L87 was not covered by tests

return cmd
}

func (cli *cliBouncers) list() error {
out := color.Output
func (cli *cliBouncers) listHuman(out io.Writer, bouncers ent.Bouncers) {
t := newLightTable(out).Writer
t.AppendHeader(table.Row{"Name", "IP Address", "Valid", "Last API pull", "Type", "Version", "Auth Type"})

for _, b := range bouncers {
revoked := emoji.CheckMark
if b.Revoked {
revoked = emoji.Prohibited
}

Check warning on line 100 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L92-L100

Added lines #L92 - L100 were not covered by tests

lastPull := ""
if b.LastPull != nil {
lastPull = b.LastPull.Format(time.RFC3339)
}

Check warning on line 105 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L102-L105

Added lines #L102 - L105 were not covered by tests

t.AppendRow(table.Row{b.Name, b.IPAddress, revoked, lastPull, b.Type, b.Version, b.AuthType})

Check warning on line 107 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L107

Added line #L107 was not covered by tests
}

fmt.Fprintln(out, t.Render())

Check warning on line 110 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L110

Added line #L110 was not covered by tests
}

// bouncerInfo contains only the data we want for inspect/list
type bouncerInfo struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Revoked bool `json:"revoked"`
IPAddress string `json:"ip_address"`
Type string `json:"type"`
Version string `json:"version"`
LastPull *time.Time `json:"last_pull"`
AuthType string `json:"auth_type"`
OS string `json:"os,omitempty"`
Featureflags []string `json:"featureflags,omitempty"`
}

func newBouncerInfo(b *ent.Bouncer) bouncerInfo {
return bouncerInfo{
CreatedAt: b.CreatedAt,
UpdatedAt: b.UpdatedAt,
Name: b.Name,
Revoked: b.Revoked,
IPAddress: b.IPAddress,
Type: b.Type,
Version: b.Version,
LastPull: b.LastPull,
AuthType: b.AuthType,
OS: b.GetOSNameAndVersion(),
Featureflags: b.GetFeatureFlagList(),
}

Check warning on line 141 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L128-L141

Added lines #L128 - L141 were not covered by tests
}

func (cli *cliBouncers) listCSV(out io.Writer, bouncers ent.Bouncers) error {
csvwriter := csv.NewWriter(out)

if err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version", "auth_type"}); err != nil {
return fmt.Errorf("failed to write raw header: %w", err)
}

Check warning on line 149 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L144-L149

Added lines #L144 - L149 were not covered by tests

for _, b := range bouncers {
valid := "validated"
if b.Revoked {
valid = "pending"
}

Check warning on line 155 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L151-L155

Added lines #L151 - L155 were not covered by tests

lastPull := ""
if b.LastPull != nil {
lastPull = b.LastPull.Format(time.RFC3339)
}

Check warning on line 160 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L157-L160

Added lines #L157 - L160 were not covered by tests

if err := csvwriter.Write([]string{b.Name, b.IPAddress, valid, lastPull, b.Type, b.Version, b.AuthType}); err != nil {
return fmt.Errorf("failed to write raw: %w", err)
}

Check warning on line 164 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L162-L164

Added lines #L162 - L164 were not covered by tests
}

csvwriter.Flush()
return nil

Check warning on line 168 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L167-L168

Added lines #L167 - L168 were not covered by tests
}


func (cli *cliBouncers) list(out io.Writer) error {

Check warning on line 172 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L172

Added line #L172 was not covered by tests
bouncers, err := cli.db.ListBouncers()
if err != nil {
return fmt.Errorf("unable to list bouncers: %w", err)
}

switch cli.cfg().Cscli.Output {
case "human":
getBouncersTable(out, bouncers)
cli.listHuman(out, bouncers)

Check warning on line 180 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L180

Added line #L180 was not covered by tests
case "json":
info := make([]bouncerInfo, 0, len(bouncers))
for _, b := range bouncers {
info = append(info, newBouncerInfo(b))
}

Check warning on line 185 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L182-L185

Added lines #L182 - L185 were not covered by tests

enc := json.NewEncoder(out)
enc.SetIndent("", " ")

if err := enc.Encode(bouncers); err != nil {
return fmt.Errorf("failed to marshal: %w", err)
if err := enc.Encode(info); err != nil {
return errors.New("failed to marshal")

Check warning on line 191 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L190-L191

Added lines #L190 - L191 were not covered by tests
}

return nil
case "raw":
csvwriter := csv.NewWriter(out)

if err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version", "auth_type"}); err != nil {
return fmt.Errorf("failed to write raw header: %w", err)
}

for _, b := range bouncers {
valid := "validated"
if b.Revoked {
valid = "pending"
}

lastPull := ""
if b.LastPull != nil {
lastPull = b.LastPull.Format(time.RFC3339)
}

if err := csvwriter.Write([]string{b.Name, b.IPAddress, valid, lastPull, b.Type, b.Version, b.AuthType}); err != nil {
return fmt.Errorf("failed to write raw: %w", err)
}
}

csvwriter.Flush()
return cli.listCSV(out, bouncers)

Check warning on line 196 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L196

Added line #L196 was not covered by tests
}

return nil
Expand All @@ -140,7 +207,7 @@
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, _ []string) error {
return cli.list()
return cli.list(color.Output)

Check warning on line 210 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L210

Added line #L210 was not covered by tests
},
}

Expand Down Expand Up @@ -206,13 +273,14 @@
return cmd
}

func (cli *cliBouncers) deleteValid(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// need to load config and db because PersistentPreRunE is not called for completions

// validBouncerID returns a list of bouncer IDs for command completion
func (cli *cliBouncers) validBouncerID(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

Check warning on line 277 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L277

Added line #L277 was not covered by tests
var err error

cfg := cli.cfg()

// need to load config and db because PersistentPreRunE is not called for completions

Check warning on line 283 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L282-L283

Added lines #L282 - L283 were not covered by tests
if err = require.LAPI(cfg); err != nil {
cobra.CompError("unable to list bouncers " + err.Error())
return nil, cobra.ShellCompDirectiveNoFileComp
Expand Down Expand Up @@ -261,7 +329,7 @@
Args: cobra.MinimumNArgs(1),
Aliases: []string{"remove"},
DisableAutoGenTag: true,
ValidArgsFunction: cli.deleteValid,
ValidArgsFunction: cli.validBouncerID,

Check warning on line 332 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L332

Added line #L332 was not covered by tests
RunE: func(_ *cobra.Command, args []string) error {
return cli.delete(args)
},
Expand Down Expand Up @@ -292,7 +360,7 @@
return nil
}

getBouncersTable(color.Output, bouncers)
cli.listHuman(color.Output, bouncers)

Check warning on line 363 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L363

Added line #L363 was not covered by tests

if !force {
if yes, err := askYesNo(
Expand Down Expand Up @@ -341,3 +409,84 @@

return cmd
}

func (cli *cliBouncers) inspectHuman(out io.Writer, bouncer *ent.Bouncer) {
t := newTable(out).Writer

t.SetTitle("Bouncer: " + bouncer.Name)

t.SetColumnConfigs([]table.ColumnConfig{
{Number: 1, AutoMerge: true},
})

lastPull := ""
if bouncer.LastPull != nil {
lastPull = bouncer.LastPull.String()
}

Check warning on line 425 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L413-L425

Added lines #L413 - L425 were not covered by tests

t.AppendRows([]table.Row{
{"Created At", bouncer.CreatedAt},
{"Last Update", bouncer.UpdatedAt},
{"Revoked?", bouncer.Revoked},
{"IP Address", bouncer.IPAddress},
{"Type", bouncer.Type},
{"Version", bouncer.Version},
{"Last Pull", lastPull},
{"Auth type", bouncer.AuthType},
{"OS", bouncer.GetOSNameAndVersion()},
})

for _, ff := range bouncer.GetFeatureFlagList() {
t.AppendRow(table.Row{"Feature Flags", ff})
}

Check warning on line 441 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L427-L441

Added lines #L427 - L441 were not covered by tests

fmt.Fprintln(out, t.Render())

Check warning on line 443 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L443

Added line #L443 was not covered by tests
}

func (cli *cliBouncers) inspect(bouncer *ent.Bouncer) error {
out := color.Output
outputFormat := cli.cfg().Cscli.Output

switch outputFormat {
case "human":
cli.inspectHuman(out, bouncer)
case "json":
enc := json.NewEncoder(out)
enc.SetIndent("", " ")

if err := enc.Encode(newBouncerInfo(bouncer)); err != nil {
return errors.New("failed to marshal")
}

Check warning on line 459 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L446-L459

Added lines #L446 - L459 were not covered by tests

return nil
default:
return fmt.Errorf("output format '%s' not supported for this command", outputFormat)

Check warning on line 463 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L461-L463

Added lines #L461 - L463 were not covered by tests
}
return nil

Check warning on line 465 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L465

Added line #L465 was not covered by tests
}


func (cli *cliBouncers) newInspectCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "inspect [bouncer_name]",
Short: "inspect a bouncer by name",
Example: `cscli bouncers inspect "bouncer1"`,
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
ValidArgsFunction: cli.validBouncerID,
RunE: func(cmd *cobra.Command, args []string) error {
bouncerName := args[0]

b, err := cli.db.Ent.Bouncer.Query().
Where(bouncer.Name(bouncerName)).
Only(cmd.Context())
if err != nil {
return fmt.Errorf("unable to read bouncer data '%s': %w", bouncerName, err)
}

Check warning on line 485 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L469-L485

Added lines #L469 - L485 were not covered by tests

return cli.inspect(b)

Check warning on line 487 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L487

Added line #L487 was not covered by tests
},
}

return cmd

Check warning on line 491 in cmd/crowdsec-cli/bouncers.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/bouncers.go#L491

Added line #L491 was not covered by tests
}
33 changes: 0 additions & 33 deletions cmd/crowdsec-cli/bouncers_table.go

This file was deleted.

Loading
Loading