Pārlūkot izejas kodu

cscli hub list: show only non-empty tables with -o human

* agent config: remove unused LintOnly bool
* Item.IsLocal() -> Item.State.IsLocal(); split method InstallStatus()
* cscli hub list: show only non-empty tables with -o human
mmetc 1 gadu atpakaļ
vecāks
revīzija
1ab4487b65

+ 2 - 2
cmd/crowdsec-cli/config_backup.go

@@ -46,7 +46,7 @@ func backupHub(dirPath string) error {
 			}
 			}
 
 
 			//for the local/tainted ones, we back up the full file
 			//for the local/tainted ones, we back up the full file
-			if v.State.Tainted || v.IsLocal() || !v.State.UpToDate {
+			if v.State.Tainted || v.State.IsLocal() || !v.State.UpToDate {
 				//we need to backup stages for parsers
 				//we need to backup stages for parsers
 				if itemType == cwhub.PARSERS || itemType == cwhub.POSTOVERFLOWS {
 				if itemType == cwhub.PARSERS || itemType == cwhub.POSTOVERFLOWS {
 					fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
 					fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
@@ -54,7 +54,7 @@ func backupHub(dirPath string) error {
 						return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
 						return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
 					}
 					}
 				}
 				}
-				clog.Debugf("[%s]: backing up file (tainted:%t local:%t up-to-date:%t)", k, v.State.Tainted, v.IsLocal(), v.State.UpToDate)
+				clog.Debugf("[%s]: backing up file (tainted:%t local:%t up-to-date:%t)", k, v.State.Tainted, v.State.IsLocal(), v.State.UpToDate)
 				tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
 				tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
 				if err = CopyFile(v.State.LocalPath, tfile); err != nil {
 				if err = CopyFile(v.State.LocalPath, tfile); err != nil {
 					return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.State.LocalPath, tfile, err)
 					return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.State.LocalPath, tfile, err)

+ 1 - 1
cmd/crowdsec-cli/hub.go

@@ -66,7 +66,7 @@ func runHubList(cmd *cobra.Command, args []string) error {
 		}
 		}
 	}
 	}
 
 
-	err = listItems(color.Output, cwhub.ItemTypes, items)
+	err = listItems(color.Output, cwhub.ItemTypes, items, true)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
cmd/crowdsec-cli/itemcommands.go

@@ -580,7 +580,7 @@ func itemsListRunner(it hubItemType) func(cmd *cobra.Command, args []string) err
 			return err
 			return err
 		}
 		}
 
 
-		if err = listItems(color.Output, []string{it.name}, items); err != nil {
+		if err = listItems(color.Output, []string{it.name}, items, false); err != nil {
 			return err
 			return err
 		}
 		}
 
 

+ 13 - 5
cmd/crowdsec-cli/items.go

@@ -53,11 +53,19 @@ func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly b
 	return items, nil
 	return items, nil
 }
 }
 
 
-func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item) error {
+func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item, omitIfEmpty bool) error {
 	switch csConfig.Cscli.Output {
 	switch csConfig.Cscli.Output {
 	case "human":
 	case "human":
+		nothingToDisplay := true
 		for _, itemType := range itemTypes {
 		for _, itemType := range itemTypes {
+			if omitIfEmpty && len(items[itemType]) == 0 {
+				continue
+			}
 			listHubItemTable(out, "\n"+strings.ToUpper(itemType), items[itemType])
 			listHubItemTable(out, "\n"+strings.ToUpper(itemType), items[itemType])
+			nothingToDisplay = false
+		}
+		if nothingToDisplay {
+			fmt.Println("No items to display")
 		}
 		}
 	case "json":
 	case "json":
 		type itemHubStatus struct {
 		type itemHubStatus struct {
@@ -75,14 +83,15 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
 			hubStatus[itemType] = make([]itemHubStatus, len(items[itemType]))
 			hubStatus[itemType] = make([]itemHubStatus, len(items[itemType]))
 
 
 			for i, item := range items[itemType] {
 			for i, item := range items[itemType] {
-				status, emo := item.InstallStatus()
+				status := item.State.Text()
+				status_emo := item.State.Emoji()
 				hubStatus[itemType][i] = itemHubStatus{
 				hubStatus[itemType][i] = itemHubStatus{
 					Name:         item.Name,
 					Name:         item.Name,
 					LocalVersion: item.State.LocalVersion,
 					LocalVersion: item.State.LocalVersion,
 					LocalPath:    item.State.LocalPath,
 					LocalPath:    item.State.LocalPath,
 					Description:  item.Description,
 					Description:  item.Description,
 					Status:       status,
 					Status:       status,
-					UTF8Status:   fmt.Sprintf("%v  %s", emo, status),
+					UTF8Status:   fmt.Sprintf("%v  %s", status_emo, status),
 				}
 				}
 			}
 			}
 		}
 		}
@@ -107,10 +116,9 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
 
 
 		for _, itemType := range itemTypes {
 		for _, itemType := range itemTypes {
 			for _, item := range items[itemType] {
 			for _, item := range items[itemType] {
-				status, _ := item.InstallStatus()
 				row := []string{
 				row := []string{
 					item.Name,
 					item.Name,
-					status,
+					item.State.Text(),
 					item.State.LocalVersion,
 					item.State.LocalVersion,
 					item.Description,
 					item.Description,
 				}
 				}

+ 1 - 1
cmd/crowdsec-cli/support.go

@@ -140,7 +140,7 @@ func collectHubItems(hub *cwhub.Hub, itemType string) []byte {
 		log.Warnf("could not collect %s list: %s", itemType, err)
 		log.Warnf("could not collect %s list: %s", itemType, err)
 	}
 	}
 
 
-	if err := listItems(out, []string{itemType}, items); err != nil {
+	if err := listItems(out, []string{itemType}, items, false); err != nil {
 		log.Warnf("could not collect %s list: %s", itemType, err)
 		log.Warnf("could not collect %s list: %s", itemType, err)
 	}
 	}
 	return out.Bytes()
 	return out.Bytes()

+ 2 - 2
cmd/crowdsec-cli/utils_table.go

@@ -18,8 +18,8 @@ func listHubItemTable(out io.Writer, title string, items []*cwhub.Item) {
 	t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
 	t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
 
 
 	for _, item := range items {
 	for _, item := range items {
-		status, emo := item.InstallStatus()
-		t.AddRow(item.Name, fmt.Sprintf("%v  %s", emo, status), item.State.LocalVersion, item.State.LocalPath)
+		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)
 	renderTableTitle(out, title)
 	t.Render()
 	t.Render()

+ 0 - 4
cmd/crowdsec/main.go

@@ -262,10 +262,6 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo
 		return nil, errors.New("You must run at least the API Server or crowdsec")
 		return nil, errors.New("You must run at least the API Server or crowdsec")
 	}
 	}
 
 
-	if flags.TestMode && !cConfig.DisableAgent {
-		cConfig.Crowdsec.LintOnly = true
-	}
-
 	if flags.OneShotDSN != "" && flags.SingleFileType == "" {
 	if flags.OneShotDSN != "" && flags.SingleFileType == "" {
 		return nil, errors.New("-dsn requires a -type argument")
 		return nil, errors.New("-dsn requires a -type argument")
 	}
 	}

+ 0 - 1
pkg/csconfig/crowdsec_service.go

@@ -23,7 +23,6 @@ type CrowdsecServiceCfg struct {
 	BucketsRoutinesCount      int               `yaml:"buckets_routines"`
 	BucketsRoutinesCount      int               `yaml:"buckets_routines"`
 	OutputRoutinesCount       int               `yaml:"output_routines"`
 	OutputRoutinesCount       int               `yaml:"output_routines"`
 	SimulationConfig          *SimulationConfig `yaml:"-"`
 	SimulationConfig          *SimulationConfig `yaml:"-"`
-	LintOnly                  bool              `yaml:"-"`                          // if set to true, exit after loading configs
 	BucketStateFile           string            `yaml:"state_input_file,omitempty"` // if we need to unserialize buckets at start
 	BucketStateFile           string            `yaml:"state_input_file,omitempty"` // if we need to unserialize buckets at start
 	BucketStateDumpDir        string            `yaml:"state_output_dir,omitempty"` // if we need to unserialize buckets on shutdown
 	BucketStateDumpDir        string            `yaml:"state_output_dir,omitempty"` // if we need to unserialize buckets on shutdown
 	BucketsGCEnabled          bool              `yaml:"-"`                          // we need to garbage collect buckets when in forensic mode
 	BucketsGCEnabled          bool              `yaml:"-"`                          // we need to garbage collect buckets when in forensic mode

+ 1 - 1
pkg/cwhub/hub.go

@@ -109,7 +109,7 @@ func (h *Hub) ItemStats() []string {
 		loaded += fmt.Sprintf("%d %s, ", len(h.GetItemMap(itemType)), itemType)
 		loaded += fmt.Sprintf("%d %s, ", len(h.GetItemMap(itemType)), itemType)
 
 
 		for _, item := range h.GetItemMap(itemType) {
 		for _, item := range h.GetItemMap(itemType) {
-			if item.IsLocal() {
+			if item.State.IsLocal() {
 				local++
 				local++
 			}
 			}
 
 

+ 44 - 49
pkg/cwhub/item.go

@@ -53,6 +53,48 @@ type ItemState struct {
 	BelongsToCollections []string `json:"belongs_to_collections,omitempty" yaml:"belongs_to_collections,omitempty"`
 	BelongsToCollections []string `json:"belongs_to_collections,omitempty" yaml:"belongs_to_collections,omitempty"`
 }
 }
 
 
+// IsLocal returns true if the item has been create by a user (not downloaded from the hub).
+func (s *ItemState) IsLocal() bool {
+	return s.Installed && !s.Downloaded
+}
+
+// Text returns the status of the item as a string (eg. "enabled,update-available").
+func (s *ItemState) Text() string {
+	ret := "disabled"
+
+	if s.Installed {
+		ret = "enabled"
+	}
+
+	if s.IsLocal() {
+		ret += ",local"
+	}
+
+	if s.Tainted {
+		ret += ",tainted"
+	} else if !s.UpToDate && !s.IsLocal() {
+		ret += ",update-available"
+	}
+
+	return ret
+}
+
+// Emoji returns the status of the item as an emoji (eg. emoji.Warning).
+func (s *ItemState) Emoji() emoji.Emoji {
+	switch {
+	case s.IsLocal():
+		return emoji.House
+	case !s.Installed:
+		return emoji.Prohibited
+	case s.Tainted || (!s.UpToDate && !s.IsLocal()):
+		return emoji.Warning
+	case s.Installed:
+		return emoji.CheckMark
+	default:
+		return emoji.QuestionMark
+	}
+}
+
 // Item is created from an index file and enriched with local info.
 // Item is created from an index file and enriched with local info.
 type Item struct {
 type Item struct {
 	hub *Hub // back pointer to the hub, to retrieve other items and call install/remove methods
 	hub *Hub // back pointer to the hub, to retrieve other items and call install/remove methods
@@ -107,11 +149,6 @@ func (i *Item) HasSubItems() bool {
 	return i.Type == COLLECTIONS
 	return i.Type == COLLECTIONS
 }
 }
 
 
-// IsLocal returns true if the item has been create by a user (not downloaded from the hub).
-func (i *Item) IsLocal() bool {
-	return i.State.Installed && !i.State.Downloaded
-}
-
 // MarshalJSON is used to prepare the output for "cscli ... inspect -o json".
 // MarshalJSON is used to prepare the output for "cscli ... inspect -o json".
 // It must not use a pointer receiver.
 // It must not use a pointer receiver.
 func (i Item) MarshalJSON() ([]byte, error) {
 func (i Item) MarshalJSON() ([]byte, error) {
@@ -139,7 +176,7 @@ func (i Item) MarshalJSON() ([]byte, error) {
 		UpToDate:             i.State.UpToDate,
 		UpToDate:             i.State.UpToDate,
 		Tainted:              i.State.Tainted,
 		Tainted:              i.State.Tainted,
 		BelongsToCollections: i.State.BelongsToCollections,
 		BelongsToCollections: i.State.BelongsToCollections,
-		Local:                i.IsLocal(),
+		Local:                i.State.IsLocal(),
 	})
 	})
 }
 }
 
 
@@ -155,7 +192,7 @@ func (i Item) MarshalYAML() (interface{}, error) {
 	}{
 	}{
 		Alias: Alias(i),
 		Alias: Alias(i),
 		State: i.State,
 		State: i.State,
-		Local: i.IsLocal(),
+		Local: i.State.IsLocal(),
 	}, nil
 	}, nil
 }
 }
 
 
@@ -290,48 +327,6 @@ func (i *Item) descendants() ([]*Item, error) {
 	return ret, nil
 	return ret, nil
 }
 }
 
 
-// InstallStatus returns the status of the item as a string and an emoji
-// (eg. "enabled,update-available" and emoji.Warning).
-func (i *Item) InstallStatus() (string, emoji.Emoji) {
-	status := "disabled"
-	ok := false
-
-	if i.State.Installed {
-		ok = true
-		status = "enabled"
-	}
-
-	managed := true
-	if i.IsLocal() {
-		managed = false
-		status += ",local"
-	}
-
-	warning := false
-	if i.State.Tainted {
-		warning = true
-		status += ",tainted"
-	} else if !i.State.UpToDate && !i.IsLocal() {
-		warning = true
-		status += ",update-available"
-	}
-
-	emo := emoji.QuestionMark
-
-	switch {
-	case !managed:
-		emo = emoji.House
-	case !i.State.Installed:
-		emo = emoji.Prohibited
-	case warning:
-		emo = emoji.Warning
-	case ok:
-		emo = emoji.CheckMark
-	}
-
-	return status, emo
-}
-
 // versionStatus returns the status of the item version compared to the hub version.
 // versionStatus returns the status of the item version compared to the hub version.
 // semver requires the 'v' prefix.
 // semver requires the 'v' prefix.
 func (i *Item) versionStatus() int {
 func (i *Item) versionStatus() int {

+ 2 - 2
pkg/cwhub/item_test.go

@@ -23,7 +23,7 @@ func TestItemStatus(t *testing.T) {
 		item.State.Tainted = false
 		item.State.Tainted = false
 		item.State.Downloaded = true
 		item.State.Downloaded = true
 
 
-		txt, _ := item.InstallStatus()
+		txt := item.State.Text()
 		require.Equal(t, "enabled,update-available", txt)
 		require.Equal(t, "enabled,update-available", txt)
 
 
 		item.State.Installed = true
 		item.State.Installed = true
@@ -31,7 +31,7 @@ func TestItemStatus(t *testing.T) {
 		item.State.Tainted = false
 		item.State.Tainted = false
 		item.State.Downloaded = false
 		item.State.Downloaded = false
 
 
-		txt, _ = item.InstallStatus()
+		txt = item.State.Text()
 		require.Equal(t, "enabled,local", txt)
 		require.Equal(t, "enabled,local", txt)
 	}
 	}
 
 

+ 1 - 1
pkg/cwhub/iteminstall.go

@@ -13,7 +13,7 @@ func (i *Item) enable() error {
 			return fmt.Errorf("%s is tainted, won't enable unless --force", i.Name)
 			return fmt.Errorf("%s is tainted, won't enable unless --force", i.Name)
 		}
 		}
 
 
-		if i.IsLocal() {
+		if i.State.IsLocal() {
 			return fmt.Errorf("%s is local, won't enable", i.Name)
 			return fmt.Errorf("%s is local, won't enable", i.Name)
 		}
 		}
 
 

+ 1 - 1
pkg/cwhub/itemremove.go

@@ -66,7 +66,7 @@ func (i *Item) disable(purge bool, force bool) (bool, error) {
 
 
 // Remove disables the item, optionally removing the downloaded content.
 // Remove disables the item, optionally removing the downloaded content.
 func (i *Item) Remove(purge bool, force bool) (bool, error) {
 func (i *Item) Remove(purge bool, force bool) (bool, error) {
-	if i.IsLocal() {
+	if i.State.IsLocal() {
 		log.Warningf("%s is a local item, please delete manually", i.Name)
 		log.Warningf("%s is a local item, please delete manually", i.Name)
 		return false, nil
 		return false, nil
 	}
 	}

+ 2 - 2
pkg/cwhub/itemupgrade.go

@@ -20,7 +20,7 @@ import (
 func (i *Item) Upgrade(force bool) (bool, error) {
 func (i *Item) Upgrade(force bool) (bool, error) {
 	updated := false
 	updated := false
 
 
-	if i.IsLocal() {
+	if i.State.IsLocal() {
 		log.Infof("not upgrading %s: local item", i.Name)
 		log.Infof("not upgrading %s: local item", i.Name)
 		return false, nil
 		return false, nil
 	}
 	}
@@ -155,7 +155,7 @@ func (i *Item) fetch() ([]byte, error) {
 
 
 // download downloads the item from the hub and writes it to the hub directory.
 // download downloads the item from the hub and writes it to the hub directory.
 func (i *Item) download(overwrite bool) (string, error) {
 func (i *Item) download(overwrite bool) (string, error) {
-	if i.IsLocal() {
+	if i.State.IsLocal() {
 		return "", fmt.Errorf("%s is local, can't download", i.Name)
 		return "", fmt.Errorf("%s is local, can't download", i.Name)
 	}
 	}
 	// if user didn't --force, don't overwrite local, tainted, up-to-date files
 	// if user didn't --force, don't overwrite local, tainted, up-to-date files

+ 1 - 1
pkg/cwhub/sync.go

@@ -420,7 +420,7 @@ func (h *Hub) localSync() error {
 		case versionFuture:
 		case versionFuture:
 			warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", item.Name, item.State.LocalVersion, item.Version))
 			warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", item.Name, item.State.LocalVersion, item.Version))
 		case versionUnknown:
 		case versionUnknown:
-			if !item.IsLocal() {
+			if !item.State.IsLocal() {
 				warnings = append(warnings, fmt.Sprintf("collection %s is tainted (latest:%s)", item.Name, item.Version))
 				warnings = append(warnings, fmt.Sprintf("collection %s is tainted (latest:%s)", item.Name, item.Version))
 			}
 			}
 		}
 		}

+ 5 - 3
test/bats/20_hub.bats

@@ -34,17 +34,19 @@ teardown() {
 
 
     # no items
     # no items
     rune -0 cscli hub list
     rune -0 cscli hub list
-    assert_output --regexp ".*PARSERS.*POSTOVERFLOWS.*SCENARIOS.*COLLECTIONS.*"
+    assert_output "No items to display"
     rune -0 cscli hub list -o json
     rune -0 cscli hub list -o json
     assert_json '{parsers:[],scenarios:[],collections:[],postoverflows:[]}'
     assert_json '{parsers:[],scenarios:[],collections:[],postoverflows:[]}'
     rune -0 cscli hub list -o raw
     rune -0 cscli hub list -o raw
     assert_output 'name,status,version,description,type'
     assert_output 'name,status,version,description,type'
 
 
-    # some items
+    # some items: with output=human, show only non-empty tables
     rune -0 cscli parsers install crowdsecurity/whitelists
     rune -0 cscli parsers install crowdsecurity/whitelists
     rune -0 cscli scenarios install crowdsecurity/telnet-bf
     rune -0 cscli scenarios install crowdsecurity/telnet-bf
     rune -0 cscli hub list
     rune -0 cscli hub list
-    assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*POSTOVERFLOWS.*SCENARIOS.*crowdsecurity/telnet-bf.*COLLECTIONS.*"
+    assert_output --regexp ".*PARSERS.*crowdsecurity/whitelists.*SCENARIOS.*crowdsecurity/telnet-bf.*"
+    refute_output --partial 'POSTOVERFLOWS'
+    refute_output --partial 'COLLECTIONS'
     rune -0 cscli hub list -o json
     rune -0 cscli hub list -o json
     rune -0 jq -e '(.parsers | length == 1) and (.scenarios | length == 1)' <(output)
     rune -0 jq -e '(.parsers | length == 1) and (.scenarios | length == 1)' <(output)
     rune -0 cscli hub list -o raw
     rune -0 cscli hub list -o raw