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

cscli hub: refact/split files; add some doc/examples #3394

Merged
merged 1 commit into from
Jan 6, 2025
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
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
Loading