Skip to content

Commit

Permalink
cscli: refact/split files; add some doc/examples
Browse files Browse the repository at this point in the history
  • Loading branch information
mmetc committed Jan 6, 2025
1 parent 5c7b957 commit 6a9832c
Show file tree
Hide file tree
Showing 19 changed files with 1,399 additions and 910 deletions.
6 changes: 5 additions & 1 deletion cmd/crowdsec-cli/clihub/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,11 @@ func (cli *cliHub) newUpgradeCmd() *cobra.Command {
Long: `
Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if you want the latest versions available.
`,
// TODO: Example
Example: `# Upgrade all the collections, scenarios etc. to the latest version in the downloaded index. Update data files too.
cscli hub upgrade
# Upgrade tainted items as well; force re-download of data files.
cscli hub upgrade --force`,
Args: cobra.NoArgs,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
Expand Down
123 changes: 0 additions & 123 deletions cmd/crowdsec-cli/cliitem/appsec.go

This file was deleted.

236 changes: 236 additions & 0 deletions cmd/crowdsec-cli/cliitem/cmdinspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
package cliitem

import (
"cmp"
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/hexops/gotextdiff"
"github.com/hexops/gotextdiff/myers"
"github.com/hexops/gotextdiff/span"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
)

func (cli cliItem) inspect(ctx context.Context, args []string, url string, diff bool, rev bool, noMetrics bool) error {
cfg := cli.cfg()

if rev && !diff {
return errors.New("--rev can only be used with --diff")
}

Check warning on line 29 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L28-L29

Added lines #L28 - L29 were not covered by tests

if url != "" {
cfg.Cscli.PrometheusUrl = url
}

Check warning on line 33 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L32-L33

Added lines #L32 - L33 were not covered by tests

var contentProvider cwhub.ContentProvider

if diff {
contentProvider = require.HubDownloader(ctx, cfg)
}

Check warning on line 39 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L38-L39

Added lines #L38 - L39 were not covered by tests

hub, err := require.Hub(cfg, log.StandardLogger())
if err != nil {
return err
}

for _, name := range args {
item := hub.GetItem(cli.name, name)
if item == nil {
return fmt.Errorf("can't find '%s' in %s", name, cli.name)
}

if diff {
fmt.Println(cli.whyTainted(ctx, hub, contentProvider, item, rev))

continue

Check warning on line 55 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L53-L55

Added lines #L53 - L55 were not covered by tests
}

if err = inspectItem(hub, item, !noMetrics, cfg.Cscli.Output, cfg.Cscli.PrometheusUrl, cfg.Cscli.Color); err != nil {
return err
}

Check warning on line 60 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L59-L60

Added lines #L59 - L60 were not covered by tests

if cli.inspectDetail != nil {
if err = cli.inspectDetail(item); err != nil {
return err
}

Check warning on line 65 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L63-L65

Added lines #L63 - L65 were not covered by tests
}
}

return nil
}

// return the diff between the installed version and the latest version
func (cli cliItem) itemDiff(ctx context.Context, item *cwhub.Item, contentProvider cwhub.ContentProvider, reverse bool) (string, error) {
if !item.State.Installed {
return "", fmt.Errorf("'%s' is not installed", item.FQName())
}

Check warning on line 76 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L73-L76

Added lines #L73 - L76 were not covered by tests

dest, err := os.CreateTemp("", "cscli-diff-*")
if err != nil {
return "", fmt.Errorf("while creating temporary file: %w", err)
}
defer os.Remove(dest.Name())

_, remoteURL, err := item.FetchContentTo(ctx, contentProvider, dest.Name())
if err != nil {
return "", err
}

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

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L78-L87

Added lines #L78 - L87 were not covered by tests

latestContent, err := os.ReadFile(dest.Name())
if err != nil {
return "", fmt.Errorf("while reading %s: %w", dest.Name(), err)
}

Check warning on line 92 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L89-L92

Added lines #L89 - L92 were not covered by tests

localContent, err := os.ReadFile(item.State.LocalPath)
if err != nil {
return "", fmt.Errorf("while reading %s: %w", item.State.LocalPath, err)
}

Check warning on line 97 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L94-L97

Added lines #L94 - L97 were not covered by tests

file1 := item.State.LocalPath
file2 := remoteURL
content1 := string(localContent)
content2 := string(latestContent)

if reverse {
file1, file2 = file2, file1
content1, content2 = content2, content1
}

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

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L99-L107

Added lines #L99 - L107 were not covered by tests

edits := myers.ComputeEdits(span.URIFromPath(file1), content1, content2)
diff := gotextdiff.ToUnified(file1, file2, content1, edits)

return fmt.Sprintf("%s", diff), nil

Check warning on line 112 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L109-L112

Added lines #L109 - L112 were not covered by tests
}

func (cli cliItem) whyTainted(ctx context.Context, hub *cwhub.Hub, contentProvider cwhub.ContentProvider, item *cwhub.Item, reverse bool) string {
if !item.State.Installed {
return fmt.Sprintf("# %s is not installed", item.FQName())
}

Check warning on line 118 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L115-L118

Added lines #L115 - L118 were not covered by tests

if !item.State.Tainted {
return fmt.Sprintf("# %s is not tainted", item.FQName())
}

Check warning on line 122 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L120-L122

Added lines #L120 - L122 were not covered by tests

if len(item.State.TaintedBy) == 0 {
return fmt.Sprintf("# %s is tainted but we don't know why. please report this as a bug", item.FQName())
}

Check warning on line 126 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L124-L126

Added lines #L124 - L126 were not covered by tests

ret := []string{
fmt.Sprintf("# Let's see why %s is tainted.", item.FQName()),
}

for _, fqsub := range item.State.TaintedBy {
ret = append(ret, fmt.Sprintf("\n-> %s\n", fqsub))

sub, err := hub.GetItemFQ(fqsub)
if err != nil {
ret = append(ret, err.Error())
}

Check warning on line 138 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L128-L138

Added lines #L128 - L138 were not covered by tests

diff, err := cli.itemDiff(ctx, sub, contentProvider, reverse)
if err != nil {
ret = append(ret, err.Error())
}

Check warning on line 143 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L140-L143

Added lines #L140 - L143 were not covered by tests

if diff != "" {
ret = append(ret, diff)
} else if len(sub.State.TaintedBy) > 0 {
taintList := strings.Join(sub.State.TaintedBy, ", ")
if sub.FQName() == taintList {
// hack: avoid message "item is tainted by itself"
continue

Check warning on line 151 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L145-L151

Added lines #L145 - L151 were not covered by tests
}

ret = append(ret, fmt.Sprintf("# %s is tainted by %s", sub.FQName(), taintList))

Check warning on line 154 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L154

Added line #L154 was not covered by tests
}
}

return strings.Join(ret, "\n")

Check warning on line 158 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L158

Added line #L158 was not covered by tests
}

func (cli cliItem) newInspectCmd() *cobra.Command {
var (
url string
diff bool
rev bool
noMetrics bool
)

cmd := &cobra.Command{
Use: cmp.Or(cli.inspectHelp.use, "inspect [item]..."),
Short: cmp.Or(cli.inspectHelp.short, "Inspect given "+cli.oneOrMore),
Long: cmp.Or(cli.inspectHelp.long, "Inspect the state of one or more "+cli.name),
Example: cli.inspectHelp.example,
Args: cobra.MinimumNArgs(1),
DisableAutoGenTag: true,
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstalledItems(cli.name, args, toComplete, cli.cfg)
},

Check warning on line 178 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L177-L178

Added lines #L177 - L178 were not covered by tests
RunE: func(cmd *cobra.Command, args []string) error {
return cli.inspect(cmd.Context(), args, url, diff, rev, noMetrics)
},
}

flags := cmd.Flags()
flags.StringVarP(&url, "url", "u", "", "Prometheus url")
flags.BoolVar(&diff, "diff", false, "Show diff with latest version (for tainted items)")
flags.BoolVar(&rev, "rev", false, "Reverse diff output")
flags.BoolVar(&noMetrics, "no-metrics", false, "Don't show metrics (when cscli.output=human)")

return cmd
}

func inspectItem(hub *cwhub.Hub, item *cwhub.Item, wantMetrics bool, output string, prometheusURL string, wantColor string) error {
// This is dirty...
// We want to show current dependencies (from content), not latest (from index).
// The item is modifed but after this function the whole hub should be thrown away.
// A cleaner way would be to copy the struct first.
item.Dependencies = item.CurrentDependencies()

switch output {
case "human", "raw":
enc := yaml.NewEncoder(os.Stdout)
enc.SetIndent(2)

if err := enc.Encode(item); err != nil {
return fmt.Errorf("unable to encode item: %w", err)
}

Check warning on line 207 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L206-L207

Added lines #L206 - L207 were not covered by tests
case "json":
b, err := json.MarshalIndent(*item, "", " ")
if err != nil {
return fmt.Errorf("unable to serialize item: %w", err)
}

Check warning on line 212 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L211-L212

Added lines #L211 - L212 were not covered by tests

fmt.Print(string(b))
}

if output != "human" {
return nil
}

if item.State.Tainted {
fmt.Println()
fmt.Printf(`This item is tainted. Use "%s %s inspect --diff %s" to see why.`, filepath.Base(os.Args[0]), item.Type, item.Name)
fmt.Println()
}

Check warning on line 225 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L222-L225

Added lines #L222 - L225 were not covered by tests

if wantMetrics {
fmt.Printf("\nCurrent metrics: \n")

if err := showMetrics(prometheusURL, hub, item, wantColor); err != nil {
return err
}

Check warning on line 232 in cmd/crowdsec-cli/cliitem/cmdinspect.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/cliitem/cmdinspect.go#L231-L232

Added lines #L231 - L232 were not covered by tests
}

return nil
}
Loading

0 comments on commit 6a9832c

Please sign in to comment.