Refact cscli hub / pkg/cwhub (part 5) (#2521)

* remove unused yaml tags
* cscli/cwhub: deduplicate, remove dead code
* log.Fatal -> fmt.Errorf
* deflate utils.go by moving functions to respective files
* indexOf() -> slices.Index()
* ItemStatus() + toEmoji() -> Item.status()
* Item.versionStatus()
* move getSHA256() to loader.go
This commit is contained in:
mmetc 2023-10-06 13:59:51 +02:00 committed by GitHub
parent 9235f55c47
commit 338141f067
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 304 additions and 347 deletions

View file

@ -60,16 +60,16 @@ func NewCapiRegisterCmd() *cobra.Command {
Short: "Register to Central API (CAPI)", Short: "Register to Central API (CAPI)",
Args: cobra.MinimumNArgs(0), Args: cobra.MinimumNArgs(0),
DisableAutoGenTag: true, DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) { RunE: func(cmd *cobra.Command, args []string) error {
var err error var err error
capiUser, err := generateID(capiUserPrefix) capiUser, err := generateID(capiUserPrefix)
if err != nil { if err != nil {
log.Fatalf("unable to generate machine id: %s", err) return fmt.Errorf("unable to generate machine id: %s", err)
} }
password := strfmt.Password(generatePassword(passwordLength)) password := strfmt.Password(generatePassword(passwordLength))
apiurl, err := url.Parse(types.CAPIBaseURL) apiurl, err := url.Parse(types.CAPIBaseURL)
if err != nil { if err != nil {
log.Fatalf("unable to parse api url %s : %s", types.CAPIBaseURL, err) return fmt.Errorf("unable to parse api url %s: %w", types.CAPIBaseURL, err)
} }
_, err = apiclient.RegisterClient(&apiclient.Config{ _, err = apiclient.RegisterClient(&apiclient.Config{
MachineID: capiUser, MachineID: capiUser,
@ -80,7 +80,7 @@ func NewCapiRegisterCmd() *cobra.Command {
}, nil) }, nil)
if err != nil { if err != nil {
log.Fatalf("api client register ('%s'): %s", types.CAPIBaseURL, err) return fmt.Errorf("api client register ('%s'): %w", types.CAPIBaseURL, err)
} }
log.Printf("Successfully registered to Central API (CAPI)") log.Printf("Successfully registered to Central API (CAPI)")
@ -103,12 +103,12 @@ func NewCapiRegisterCmd() *cobra.Command {
} }
apiConfigDump, err := yaml.Marshal(apiCfg) apiConfigDump, err := yaml.Marshal(apiCfg)
if err != nil { if err != nil {
log.Fatalf("unable to marshal api credentials: %s", err) return fmt.Errorf("unable to marshal api credentials: %w", err)
} }
if dumpFile != "" { if dumpFile != "" {
err = os.WriteFile(dumpFile, apiConfigDump, 0600) err = os.WriteFile(dumpFile, apiConfigDump, 0600)
if err != nil { if err != nil {
log.Fatalf("write api credentials in '%s' failed: %s", dumpFile, err) return fmt.Errorf("write api credentials in '%s' failed: %w", dumpFile, err)
} }
log.Printf("Central API credentials dumped to '%s'", dumpFile) log.Printf("Central API credentials dumped to '%s'", dumpFile)
} else { } else {
@ -116,6 +116,8 @@ func NewCapiRegisterCmd() *cobra.Command {
} }
log.Warning(ReloadMessage()) log.Warning(ReloadMessage())
return nil
}, },
} }
cmdCapiRegister.Flags().StringVarP(&outputFile, "file", "f", "", "output file destination") cmdCapiRegister.Flags().StringVarP(&outputFile, "file", "f", "", "output file destination")
@ -133,53 +135,56 @@ func NewCapiStatusCmd() *cobra.Command {
Short: "Check status with the Central API (CAPI)", Short: "Check status with the Central API (CAPI)",
Args: cobra.MinimumNArgs(0), Args: cobra.MinimumNArgs(0),
DisableAutoGenTag: true, DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) { RunE: func(cmd *cobra.Command, args []string) error {
if csConfig.API.Server.OnlineClient == nil { if csConfig.API.Server.OnlineClient == nil {
log.Fatalf("Please provide credentials for the Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath) return fmt.Errorf("please provide credentials for the Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath)
} }
if csConfig.API.Server.OnlineClient.Credentials == nil { if csConfig.API.Server.OnlineClient.Credentials == nil {
log.Fatalf("no credentials for Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath) return fmt.Errorf("no credentials for Central API (CAPI) in '%s'", csConfig.API.Server.OnlineClient.CredentialsFilePath)
} }
password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password) password := strfmt.Password(csConfig.API.Server.OnlineClient.Credentials.Password)
apiurl, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL) apiurl, err := url.Parse(csConfig.API.Server.OnlineClient.Credentials.URL)
if err != nil { if err != nil {
log.Fatalf("parsing api url ('%s'): %s", csConfig.API.Server.OnlineClient.Credentials.URL, err) return fmt.Errorf("parsing api url ('%s'): %w", csConfig.API.Server.OnlineClient.Credentials.URL, err)
} }
if err := csConfig.LoadHub(); err != nil { if err := require.Hub(csConfig); err != nil {
log.Fatal(err) return err
} }
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Info("Run 'sudo cscli hub update' to get the hub index")
log.Fatalf("Failed to load hub index : %s", err)
}
scenarios, err := cwhub.GetInstalledItemsAsString(cwhub.SCENARIOS) scenarios, err := cwhub.GetInstalledItemsAsString(cwhub.SCENARIOS)
if err != nil { if err != nil {
log.Fatalf("failed to get scenarios : %s", err) return fmt.Errorf("failed to get scenarios: %w", err)
} }
if len(scenarios) == 0 { if len(scenarios) == 0 {
log.Fatalf("no scenarios installed, abort") return fmt.Errorf("no scenarios installed, abort")
} }
Client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", version.String()), nil) Client, err = apiclient.NewDefaultClient(apiurl, CAPIURLPrefix, fmt.Sprintf("crowdsec/%s", version.String()), nil)
if err != nil { if err != nil {
log.Fatalf("init default client: %s", err) return fmt.Errorf("init default client: %w", err)
} }
t := models.WatcherAuthRequest{ t := models.WatcherAuthRequest{
MachineID: &csConfig.API.Server.OnlineClient.Credentials.Login, MachineID: &csConfig.API.Server.OnlineClient.Credentials.Login,
Password: &password, Password: &password,
Scenarios: scenarios, Scenarios: scenarios,
} }
log.Infof("Loaded credentials from %s", csConfig.API.Server.OnlineClient.CredentialsFilePath) log.Infof("Loaded credentials from %s", csConfig.API.Server.OnlineClient.CredentialsFilePath)
log.Infof("Trying to authenticate with username %s on %s", csConfig.API.Server.OnlineClient.Credentials.Login, apiurl) log.Infof("Trying to authenticate with username %s on %s", csConfig.API.Server.OnlineClient.Credentials.Login, apiurl)
_, _, err = Client.Auth.AuthenticateWatcher(context.Background(), t) _, _, err = Client.Auth.AuthenticateWatcher(context.Background(), t)
if err != nil { if err != nil {
log.Fatalf("Failed to authenticate to Central API (CAPI) : %s", err) return fmt.Errorf("failed to authenticate to Central API (CAPI): %w", err)
} }
log.Infof("You can successfully interact with Central API (CAPI)") log.Infof("You can successfully interact with Central API (CAPI)")
return nil
}, },
} }

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -8,9 +9,75 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
) )
func backupHub(dirPath string) error {
var err error
var itemDirectory string
var upstreamParsers []string
for _, itemType := range cwhub.ItemTypes {
clog := log.WithFields(log.Fields{
"type": itemType,
})
itemMap := cwhub.GetItemMap(itemType)
if itemMap == nil {
clog.Infof("No %s to backup.", itemType)
continue
}
itemDirectory = fmt.Sprintf("%s/%s/", dirPath, itemType)
if err := os.MkdirAll(itemDirectory, os.ModePerm); err != nil {
return fmt.Errorf("error while creating %s : %s", itemDirectory, err)
}
upstreamParsers = []string{}
for k, v := range itemMap {
clog = clog.WithFields(log.Fields{
"file": v.Name,
})
if !v.Installed { //only backup installed ones
clog.Debugf("[%s] : not installed", k)
continue
}
//for the local/tainted ones, we backup the full file
if v.Tainted || v.Local || !v.UpToDate {
//we need to backup stages for parsers
if itemType == cwhub.PARSERS || itemType == cwhub.PARSERS_OVFLW {
fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
if err := os.MkdirAll(fstagedir, os.ModePerm); err != nil {
return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
}
}
clog.Debugf("[%s] : backuping file (tainted:%t local:%t up-to-date:%t)", k, v.Tainted, v.Local, v.UpToDate)
tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
if err = CopyFile(v.LocalPath, tfile); err != nil {
return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.LocalPath, tfile, err)
}
clog.Infof("local/tainted saved %s to %s", v.LocalPath, tfile)
continue
}
clog.Debugf("[%s] : from hub, just backup name (up-to-date:%t)", k, v.UpToDate)
clog.Infof("saving, version:%s, up-to-date:%t", v.Version, v.UpToDate)
upstreamParsers = append(upstreamParsers, v.Name)
}
//write the upstream items
upstreamParsersFname := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itemType)
upstreamParsersContent, err := json.MarshalIndent(upstreamParsers, "", " ")
if err != nil {
return fmt.Errorf("failed marshaling upstream parsers : %s", err)
}
err = os.WriteFile(upstreamParsersFname, upstreamParsersContent, 0644)
if err != nil {
return fmt.Errorf("unable to write to %s %s : %s", itemType, upstreamParsersFname, err)
}
clog.Infof("Wrote %d entries for %s to %s", len(upstreamParsers), itemType, upstreamParsersFname)
}
return nil
}
/* /*
Backup crowdsec configurations to directory <dirPath>: Backup crowdsec configurations to directory <dirPath>:
@ -122,7 +189,7 @@ func backupConfigToDirectory(dirPath string) error {
log.Infof("Saved profiles to %s", backupProfiles) log.Infof("Saved profiles to %s", backupProfiles)
} }
if err = BackupHub(dirPath); err != nil { if err = backupHub(dirPath); err != nil {
return fmt.Errorf("failed to backup hub config: %s", err) return fmt.Errorf("failed to backup hub config: %s", err)
} }

View file

@ -13,6 +13,7 @@ import (
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
"github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
) )
type OldAPICfg struct { type OldAPICfg struct {
@ -20,6 +21,125 @@ type OldAPICfg struct {
Password string `json:"password"` Password string `json:"password"`
} }
// it's a rip of the cli version, but in silent-mode
func silentInstallItem(name string, obtype string) (string, error) {
var item = cwhub.GetItem(obtype, name)
if item == nil {
return "", fmt.Errorf("error retrieving item")
}
it := *item
if downloadOnly && it.Downloaded && it.UpToDate {
return fmt.Sprintf("%s is already downloaded and up-to-date", it.Name), nil
}
it, err := cwhub.DownloadLatest(csConfig.Hub, it, forceAction, false)
if err != nil {
return "", fmt.Errorf("error while downloading %s : %v", it.Name, err)
}
if err := cwhub.AddItem(obtype, it); err != nil {
return "", err
}
if downloadOnly {
return fmt.Sprintf("Downloaded %s to %s", it.Name, csConfig.Cscli.HubDir+"/"+it.RemotePath), nil
}
it, err = cwhub.EnableItem(csConfig.Hub, it)
if err != nil {
return "", fmt.Errorf("error while enabling %s : %v", it.Name, err)
}
if err := cwhub.AddItem(obtype, it); err != nil {
return "", err
}
return fmt.Sprintf("Enabled %s", it.Name), nil
}
func restoreHub(dirPath string) error {
var err error
if err := csConfig.LoadHub(); err != nil {
return err
}
cwhub.SetHubBranch()
for _, itype := range cwhub.ItemTypes {
itemDirectory := fmt.Sprintf("%s/%s/", dirPath, itype)
if _, err = os.Stat(itemDirectory); err != nil {
log.Infof("no %s in backup", itype)
continue
}
/*restore the upstream items*/
upstreamListFN := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itype)
file, err := os.ReadFile(upstreamListFN)
if err != nil {
return fmt.Errorf("error while opening %s : %s", upstreamListFN, err)
}
var upstreamList []string
err = json.Unmarshal(file, &upstreamList)
if err != nil {
return fmt.Errorf("error unmarshaling %s : %s", upstreamListFN, err)
}
for _, toinstall := range upstreamList {
label, err := silentInstallItem(toinstall, itype)
if err != nil {
log.Errorf("Error while installing %s : %s", toinstall, err)
} else if label != "" {
log.Infof("Installed %s : %s", toinstall, label)
} else {
log.Printf("Installed %s : ok", toinstall)
}
}
/*restore the local and tainted items*/
files, err := os.ReadDir(itemDirectory)
if err != nil {
return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory, err)
}
for _, file := range files {
//this was the upstream data
if file.Name() == fmt.Sprintf("upstream-%s.json", itype) {
continue
}
if itype == cwhub.PARSERS || itype == cwhub.PARSERS_OVFLW {
//we expect a stage here
if !file.IsDir() {
continue
}
stage := file.Name()
stagedir := fmt.Sprintf("%s/%s/%s/", csConfig.ConfigPaths.ConfigDir, itype, stage)
log.Debugf("Found stage %s in %s, target directory : %s", stage, itype, stagedir)
if err = os.MkdirAll(stagedir, os.ModePerm); err != nil {
return fmt.Errorf("error while creating stage directory %s : %s", stagedir, err)
}
/*find items*/
ifiles, err := os.ReadDir(itemDirectory + "/" + stage + "/")
if err != nil {
return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory+"/"+stage, err)
}
//finally copy item
for _, tfile := range ifiles {
log.Infof("Going to restore local/tainted [%s]", tfile.Name())
sourceFile := fmt.Sprintf("%s/%s/%s", itemDirectory, stage, tfile.Name())
destinationFile := fmt.Sprintf("%s%s", stagedir, tfile.Name())
if err = CopyFile(sourceFile, destinationFile); err != nil {
return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
}
log.Infof("restored %s to %s", sourceFile, destinationFile)
}
} else {
log.Infof("Going to restore local/tainted [%s]", file.Name())
sourceFile := fmt.Sprintf("%s/%s", itemDirectory, file.Name())
destinationFile := fmt.Sprintf("%s/%s/%s", csConfig.ConfigPaths.ConfigDir, itype, file.Name())
if err = CopyFile(sourceFile, destinationFile); err != nil {
return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
}
log.Infof("restored %s to %s", sourceFile, destinationFile)
}
}
}
return nil
}
/* /*
Restore crowdsec configurations to directory <dirPath>: Restore crowdsec configurations to directory <dirPath>:
@ -168,7 +288,7 @@ func restoreConfigFromDirectory(dirPath string, oldBackup bool) error {
} }
} }
if err = RestoreHub(dirPath); err != nil { if err = restoreHub(dirPath); err != nil {
return fmt.Errorf("failed to restore hub config : %s", err) return fmt.Errorf("failed to restore hub config : %s", err)
} }

View file

@ -88,9 +88,8 @@ Fetches the [.index.json](https://github.com/crowdsecurity/hub/blob/master/.inde
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := cwhub.SetHubBranch(); err != nil { cwhub.SetHubBranch()
return fmt.Errorf("error while setting hub branch: %s", err)
}
return nil return nil
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
@ -134,9 +133,8 @@ Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := cwhub.SetHubBranch(); err != nil { cwhub.SetHubBranch()
return fmt.Errorf("error while setting hub branch: %s", err)
}
return nil return nil
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -73,9 +73,7 @@ func Hub (c *csconfig.Config) error {
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := cwhub.SetHubBranch(); err != nil { cwhub.SetHubBranch()
return fmt.Errorf("while setting hub branch: %w", err)
}
if err := cwhub.GetHubIdx(c.Hub); err != nil { if err := cwhub.GetHubIdx(c.Hub); err != nil {
return fmt.Errorf("failed to read Hub index: '%w'. Run 'sudo cscli hub update' to download the index again", err) return fmt.Errorf("failed to read Hub index: '%w'. Run 'sudo cscli hub update' to download the index again", err)

View file

@ -19,7 +19,7 @@ func addToExclusion(name string) error {
} }
func removeFromExclusion(name string) error { func removeFromExclusion(name string) error {
index := indexOf(name, csConfig.Cscli.SimulationConfig.Exclusions) index := slices.Index(csConfig.Cscli.SimulationConfig.Exclusions, name)
// Remove element from the slice // Remove element from the slice
csConfig.Cscli.SimulationConfig.Exclusions[index] = csConfig.Cscli.SimulationConfig.Exclusions[len(csConfig.Cscli.SimulationConfig.Exclusions)-1] csConfig.Cscli.SimulationConfig.Exclusions[index] = csConfig.Cscli.SimulationConfig.Exclusions[len(csConfig.Cscli.SimulationConfig.Exclusions)-1]

View file

@ -8,7 +8,6 @@ import (
"math" "math"
"net" "net"
"net/http" "net/http"
"os"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@ -24,6 +23,7 @@ import (
"github.com/crowdsecurity/go-cs-lib/trace" "github.com/crowdsecurity/go-cs-lib/trace"
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
"github.com/crowdsecurity/crowdsec/pkg/cwhub" "github.com/crowdsecurity/crowdsec/pkg/cwhub"
"github.com/crowdsecurity/crowdsec/pkg/database" "github.com/crowdsecurity/crowdsec/pkg/database"
"github.com/crowdsecurity/crowdsec/pkg/types" "github.com/crowdsecurity/crowdsec/pkg/types"
@ -38,34 +38,6 @@ func printHelp(cmd *cobra.Command) {
} }
} }
func indexOf(s string, slice []string) int {
for i, elem := range slice {
if s == elem {
return i
}
}
return -1
}
func LoadHub() error {
if err := csConfig.LoadHub(); err != nil {
log.Fatal(err)
}
if csConfig.Hub == nil {
return fmt.Errorf("unable to load hub")
}
if err := cwhub.SetHubBranch(); err != nil {
log.Warningf("unable to set hub branch (%s), default to master", err)
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
return fmt.Errorf("Failed to get Hub index : '%w'. Run 'sudo cscli hub update' to get the hub index", err)
}
return nil
}
func Suggest(itemType string, baseItem string, suggestItem string, score int, ignoreErr bool) { func Suggest(itemType string, baseItem string, suggestItem string, score int, ignoreErr bool) {
errMsg := "" errMsg := ""
if score < MaxDistance { if score < MaxDistance {
@ -100,7 +72,7 @@ func GetDistance(itemType string, itemName string) (*cwhub.Item, int) {
} }
func compAllItems(itemType string, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func compAllItems(itemType string, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if err := LoadHub(); err != nil { if err := require.Hub(csConfig); err != nil {
return nil, cobra.ShellCompDirectiveDefault return nil, cobra.ShellCompDirectiveDefault
} }
@ -116,7 +88,7 @@ func compAllItems(itemType string, args []string, toComplete string) ([]string,
} }
func compInstalledItems(itemType string, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func compInstalledItems(itemType string, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if err := LoadHub(); err != nil { if err := require.Hub(csConfig); err != nil {
return nil, cobra.ShellCompDirectiveDefault return nil, cobra.ShellCompDirectiveDefault
} }
@ -453,37 +425,6 @@ func GetScenarioMetric(url string, itemName string) map[string]int {
return stats return stats
} }
// it's a rip of the cli version, but in silent-mode
func silenceInstallItem(name string, obtype string) (string, error) {
var item = cwhub.GetItem(obtype, name)
if item == nil {
return "", fmt.Errorf("error retrieving item")
}
it := *item
if downloadOnly && it.Downloaded && it.UpToDate {
return fmt.Sprintf("%s is already downloaded and up-to-date", it.Name), nil
}
it, err := cwhub.DownloadLatest(csConfig.Hub, it, forceAction, false)
if err != nil {
return "", fmt.Errorf("error while downloading %s : %v", it.Name, err)
}
if err := cwhub.AddItem(obtype, it); err != nil {
return "", err
}
if downloadOnly {
return fmt.Sprintf("Downloaded %s to %s", it.Name, csConfig.Cscli.HubDir+"/"+it.RemotePath), nil
}
it, err = cwhub.EnableItem(csConfig.Hub, it)
if err != nil {
return "", fmt.Errorf("error while enabling %s : %v", it.Name, err)
}
if err := cwhub.AddItem(obtype, it); err != nil {
return "", err
}
return fmt.Sprintf("Enabled %s", it.Name), nil
}
func GetPrometheusMetric(url string) []*prom2json.Family { func GetPrometheusMetric(url string) []*prom2json.Family {
mfChan := make(chan *dto.MetricFamily, 1024) mfChan := make(chan *dto.MetricFamily, 1024)
@ -512,160 +453,6 @@ func GetPrometheusMetric(url string) []*prom2json.Family {
return result return result
} }
func RestoreHub(dirPath string) error {
var err error
if err := csConfig.LoadHub(); err != nil {
return err
}
if err := cwhub.SetHubBranch(); err != nil {
return fmt.Errorf("error while setting hub branch: %s", err)
}
for _, itype := range cwhub.ItemTypes {
itemDirectory := fmt.Sprintf("%s/%s/", dirPath, itype)
if _, err = os.Stat(itemDirectory); err != nil {
log.Infof("no %s in backup", itype)
continue
}
/*restore the upstream items*/
upstreamListFN := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itype)
file, err := os.ReadFile(upstreamListFN)
if err != nil {
return fmt.Errorf("error while opening %s : %s", upstreamListFN, err)
}
var upstreamList []string
err = json.Unmarshal(file, &upstreamList)
if err != nil {
return fmt.Errorf("error unmarshaling %s : %s", upstreamListFN, err)
}
for _, toinstall := range upstreamList {
label, err := silenceInstallItem(toinstall, itype)
if err != nil {
log.Errorf("Error while installing %s : %s", toinstall, err)
} else if label != "" {
log.Infof("Installed %s : %s", toinstall, label)
} else {
log.Printf("Installed %s : ok", toinstall)
}
}
/*restore the local and tainted items*/
files, err := os.ReadDir(itemDirectory)
if err != nil {
return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory, err)
}
for _, file := range files {
//this was the upstream data
if file.Name() == fmt.Sprintf("upstream-%s.json", itype) {
continue
}
if itype == cwhub.PARSERS || itype == cwhub.PARSERS_OVFLW {
//we expect a stage here
if !file.IsDir() {
continue
}
stage := file.Name()
stagedir := fmt.Sprintf("%s/%s/%s/", csConfig.ConfigPaths.ConfigDir, itype, stage)
log.Debugf("Found stage %s in %s, target directory : %s", stage, itype, stagedir)
if err = os.MkdirAll(stagedir, os.ModePerm); err != nil {
return fmt.Errorf("error while creating stage directory %s : %s", stagedir, err)
}
/*find items*/
ifiles, err := os.ReadDir(itemDirectory + "/" + stage + "/")
if err != nil {
return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory+"/"+stage, err)
}
//finally copy item
for _, tfile := range ifiles {
log.Infof("Going to restore local/tainted [%s]", tfile.Name())
sourceFile := fmt.Sprintf("%s/%s/%s", itemDirectory, stage, tfile.Name())
destinationFile := fmt.Sprintf("%s%s", stagedir, tfile.Name())
if err = CopyFile(sourceFile, destinationFile); err != nil {
return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
}
log.Infof("restored %s to %s", sourceFile, destinationFile)
}
} else {
log.Infof("Going to restore local/tainted [%s]", file.Name())
sourceFile := fmt.Sprintf("%s/%s", itemDirectory, file.Name())
destinationFile := fmt.Sprintf("%s/%s/%s", csConfig.ConfigPaths.ConfigDir, itype, file.Name())
if err = CopyFile(sourceFile, destinationFile); err != nil {
return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
}
log.Infof("restored %s to %s", sourceFile, destinationFile)
}
}
}
return nil
}
func BackupHub(dirPath string) error {
var err error
var itemDirectory string
var upstreamParsers []string
for _, itemType := range cwhub.ItemTypes {
clog := log.WithFields(log.Fields{
"type": itemType,
})
itemMap := cwhub.GetItemMap(itemType)
if itemMap == nil {
clog.Infof("No %s to backup.", itemType)
continue
}
itemDirectory = fmt.Sprintf("%s/%s/", dirPath, itemType)
if err := os.MkdirAll(itemDirectory, os.ModePerm); err != nil {
return fmt.Errorf("error while creating %s : %s", itemDirectory, err)
}
upstreamParsers = []string{}
for k, v := range itemMap {
clog = clog.WithFields(log.Fields{
"file": v.Name,
})
if !v.Installed { //only backup installed ones
clog.Debugf("[%s] : not installed", k)
continue
}
//for the local/tainted ones, we backup the full file
if v.Tainted || v.Local || !v.UpToDate {
//we need to backup stages for parsers
if itemType == cwhub.PARSERS || itemType == cwhub.PARSERS_OVFLW {
fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
if err := os.MkdirAll(fstagedir, os.ModePerm); err != nil {
return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
}
}
clog.Debugf("[%s] : backuping file (tainted:%t local:%t up-to-date:%t)", k, v.Tainted, v.Local, v.UpToDate)
tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
if err = CopyFile(v.LocalPath, tfile); err != nil {
return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.LocalPath, tfile, err)
}
clog.Infof("local/tainted saved %s to %s", v.LocalPath, tfile)
continue
}
clog.Debugf("[%s] : from hub, just backup name (up-to-date:%t)", k, v.UpToDate)
clog.Infof("saving, version:%s, up-to-date:%t", v.Version, v.UpToDate)
upstreamParsers = append(upstreamParsers, v.Name)
}
//write the upstream items
upstreamParsersFname := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itemType)
upstreamParsersContent, err := json.MarshalIndent(upstreamParsers, "", " ")
if err != nil {
return fmt.Errorf("failed marshaling upstream parsers : %s", err)
}
err = os.WriteFile(upstreamParsersFname, upstreamParsersContent, 0644)
if err != nil {
return fmt.Errorf("unable to write to %s %s : %s", itemType, upstreamParsersFname, err)
}
clog.Infof("Wrote %d entries for %s to %s", len(upstreamParsers), itemType, upstreamParsersFname)
}
return nil
}
type unit struct { type unit struct {
value int64 value int64
symbol string symbol string

View file

@ -17,7 +17,7 @@ func listHubItemTable(out io.Writer, title string, statuses []cwhub.ItemHubStatu
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
for _, status := range statuses { for _, status := range statuses {
t.AddRow(status.Name, status.UTF8_Status, status.LocalVersion, status.LocalPath) t.AddRow(status.Name, status.UTF8Status, status.LocalVersion, status.LocalPath)
} }
renderTableTitle(out, title) renderTableTitle(out, title)
t.Render() t.Render()

View file

@ -2,10 +2,10 @@ package csconfig
/*cscli specific config, such as hub directory*/ /*cscli specific config, such as hub directory*/
type Hub struct { type Hub struct {
HubDir string `yaml:"-"` HubDir string
ConfigDir string `yaml:"-"` ConfigDir string
HubIndexFile string `yaml:"-"` HubIndexFile string
DataDir string `yaml:"-"` DataDir string
} }
func (c *Config) LoadHub() error { func (c *Config) LoadHub() error {

View file

@ -1,9 +1,7 @@
package cwhub package cwhub
import ( import (
"crypto/sha256"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -40,7 +38,7 @@ type ItemHubStatus struct {
LocalVersion string `json:"local_version"` LocalVersion string `json:"local_version"`
LocalPath string `json:"local_path"` LocalPath string `json:"local_path"`
Description string `json:"description"` Description string `json:"description"`
UTF8_Status string `json:"utf8_status"` UTF8Status string `json:"utf8_status"`
Status string `json:"status"` Status string `json:"status"`
} }
@ -78,29 +76,48 @@ type Item struct {
Collections []string `json:"collections,omitempty" yaml:"collections,omitempty"` Collections []string `json:"collections,omitempty" yaml:"collections,omitempty"`
} }
func toEmoji(managed bool, installed bool, warning bool, ok bool) emoji.Emoji { func (i *Item) status() (string, emoji.Emoji) {
if !managed { status := "disabled"
return emoji.House ok := false
if i.Installed {
ok = true
status = "enabled"
} }
if !installed { managed := true
return emoji.Prohibited if i.Local {
managed = false
status += ",local"
} }
if warning { warning := false
return emoji.Warning if i.Tainted {
warning = true
status += ",tainted"
} else if !i.UpToDate && !i.Local {
warning = true
status += ",update-available"
} }
if ok { emo := emoji.QuestionMark
return emoji.CheckMark
switch {
case !managed:
emo = emoji.House
case !i.Installed:
emo = emoji.Prohibited
case warning:
emo = emoji.Warning
case ok:
emo = emoji.CheckMark
} }
// XXX: this is new return status, emo
return emoji.QuestionMark
} }
func (i *Item) toHubStatus() ItemHubStatus { func (i *Item) hubStatus() ItemHubStatus {
status, ok, warning, managed := ItemStatus(*i) status, emo := i.status()
return ItemHubStatus{ return ItemHubStatus{
Name: i.Name, Name: i.Name,
@ -108,37 +125,21 @@ func (i *Item) toHubStatus() ItemHubStatus {
LocalPath: i.LocalPath, LocalPath: i.LocalPath,
Description: i.Description, Description: i.Description,
Status: status, Status: status,
UTF8_Status: fmt.Sprintf("%v %s", toEmoji(managed, i.Installed, warning, ok), status), UTF8Status: fmt.Sprintf("%v %s", emo, status),
} }
} }
// versionStatus: semver requires 'v' prefix
func (i *Item) versionStatus() int {
return semver.Compare("v"+i.Version, "v"+i.LocalVersion)
}
// XXX: can we remove these globals? // XXX: can we remove these globals?
var skippedLocal = 0 var skippedLocal = 0
var skippedTainted = 0 var skippedTainted = 0
var ReferenceMissingError = errors.New("Reference(s) missing in collection") var ReferenceMissingError = errors.New("Reference(s) missing in collection")
// GetVersionStatus: semver requires 'v' prefix
func GetVersionStatus(v *Item) int {
return semver.Compare("v"+v.Version, "v"+v.LocalVersion)
}
func getSHA256(filepath string) (string, error) {
f, err := os.Open(filepath)
if err != nil {
return "", fmt.Errorf("unable to open '%s': %w", filepath, err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", fmt.Errorf("unable to calculate sha256 of '%s': %w", filepath, err)
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
func GetItemMap(itemType string) map[string]Item { func GetItemMap(itemType string) map[string]Item {
m, ok := hubIdx[itemType] m, ok := hubIdx[itemType]
if !ok { if !ok {
@ -223,35 +224,6 @@ func DisplaySummary() {
} }
} }
// returns: human-text, Enabled, Warning, Unmanaged
func ItemStatus(v Item) (string, bool, bool, bool) {
strret := "disabled"
Ok := false
if v.Installed {
Ok = true
strret = "enabled"
}
Managed := true
if v.Local {
Managed = false
strret += ",local"
}
// tainted or out of date
Warning := false
if v.Tainted {
Warning = true
strret += ",tainted"
} else if !v.UpToDate && !v.Local {
Warning = true
strret += ",update-available"
}
return strret, Ok, Warning, Managed
}
func GetInstalledItems(itemType string) ([]Item, error) { func GetInstalledItems(itemType string) ([]Item, error) {
var retItems []Item var retItems []Item
@ -305,7 +277,7 @@ func GetHubStatusForItemType(itemType string, name string, all bool) []ItemHubSt
continue continue
} }
// Check the item status // Check the item status
ret = append(ret, item.toHubStatus()) ret = append(ret, item.hubStatus())
} }
sort.Slice(ret, func(i, j int) bool { return ret[i].Name < ret[j].Name }) sort.Slice(ret, func(i, j int) bool { return ret[i].Name < ret[j].Name })

View file

@ -57,7 +57,7 @@ func TestItemStatus(t *testing.T) {
item.Local = false item.Local = false
item.Tainted = false item.Tainted = false
txt, _, _, _ := ItemStatus(*item) txt, _ := item.status()
if txt != "enabled,update-available" { if txt != "enabled,update-available" {
t.Fatalf("got '%s'", txt) t.Fatalf("got '%s'", txt)
} }
@ -67,7 +67,7 @@ func TestItemStatus(t *testing.T) {
item.Local = true item.Local = true
item.Tainted = false item.Tainted = false
txt, _, _, _ = ItemStatus(*item) txt, _ = item.status()
if txt != "disabled,local" { if txt != "disabled,local" {
t.Fatalf("got '%s'", txt) t.Fatalf("got '%s'", txt)
} }

View file

@ -13,29 +13,29 @@ import (
) )
// pick a hub branch corresponding to the current crowdsec version. // pick a hub branch corresponding to the current crowdsec version.
func chooseHubBranch() (string, error) { func chooseHubBranch() string {
latest, err := cwversion.Latest() latest, err := cwversion.Latest()
if err != nil { if err != nil {
log.Warningf("Unable to retrieve latest crowdsec version: %s, defaulting to master", err) log.Warningf("Unable to retrieve latest crowdsec version: %s, defaulting to master", err)
//lint:ignore nilerr //lint:ignore nilerr
return "master", nil return "master"
} }
csVersion := cwversion.VersionStrip() csVersion := cwversion.VersionStrip()
if csVersion == latest { if csVersion == latest {
log.Debugf("current version is equal to latest (%s)", csVersion) log.Debugf("current version is equal to latest (%s)", csVersion)
return "master", nil return "master"
} }
// if current version is greater than the latest we are in pre-release // if current version is greater than the latest we are in pre-release
if semver.Compare(csVersion, latest) == 1 { if semver.Compare(csVersion, latest) == 1 {
log.Debugf("Your current crowdsec version seems to be a pre-release (%s)", csVersion) log.Debugf("Your current crowdsec version seems to be a pre-release (%s)", csVersion)
return "master", nil return "master"
} }
if csVersion == "" { if csVersion == "" {
log.Warning("Crowdsec version is not set, using master branch for the hub") log.Warning("Crowdsec version is not set, using master branch for the hub")
return "master", nil return "master"
} }
log.Warnf("Crowdsec is not the latest version. "+ log.Warnf("Crowdsec is not the latest version. "+
@ -45,26 +45,20 @@ func chooseHubBranch() (string, error) {
log.Warnf("As a result, you will not be able to use parsers/scenarios/collections "+ log.Warnf("As a result, you will not be able to use parsers/scenarios/collections "+
"added to Crowdsec Hub after CrowdSec %s", latest) "added to Crowdsec Hub after CrowdSec %s", latest)
return csVersion, nil return csVersion
} }
// SetHubBranch sets the package variable that points to the hub branch. // SetHubBranch sets the package variable that points to the hub branch.
func SetHubBranch() error { func SetHubBranch() {
// a branch is already set, or specified from the flags // a branch is already set, or specified from the flags
if HubBranch != "" { if HubBranch != "" {
return nil return
} }
// use the branch corresponding to the crowdsec version // use the branch corresponding to the crowdsec version
branch, err := chooseHubBranch() HubBranch = chooseHubBranch()
if err != nil {
return err
}
HubBranch = branch
log.Debugf("Using branch '%s' for the hub", HubBranch) log.Debugf("Using branch '%s' for the hub", HubBranch)
return nil
} }
func InstallItem(csConfig *csconfig.Config, name string, obtype string, force bool, downloadOnly bool) error { func InstallItem(csConfig *csconfig.Config, name string, obtype string, force bool, downloadOnly bool) error {

View file

@ -1,9 +1,11 @@
package cwhub package cwhub
import ( import (
"crypto/sha256"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -43,6 +45,22 @@ func handleSymlink(path string) (string, error) {
return hubpath, nil return hubpath, nil
} }
func getSHA256(filepath string) (string, error) {
f, err := os.Open(filepath)
if err != nil {
return "", fmt.Errorf("unable to open '%s': %w", filepath, err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", fmt.Errorf("unable to calculate sha256 of '%s': %w", filepath, err)
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
type walker struct { type walker struct {
// the walk/parserVisit function can't receive extra args // the walk/parserVisit function can't receive extra args
hubdir string hubdir string
@ -317,7 +335,7 @@ func (w walker) parserVisit(path string, f os.DirEntry, err error) error {
} }
func CollecDepsCheck(v *Item) error { func CollecDepsCheck(v *Item) error {
if GetVersionStatus(v) != 0 { // not up-to-date if v.versionStatus() != 0 { // not up-to-date
log.Debugf("%s dependencies not checked : not up-to-date", v.Name) log.Debugf("%s dependencies not checked : not up-to-date", v.Name)
return nil return nil
} }
@ -415,8 +433,8 @@ func SyncDir(hub *csconfig.Hub, dir string) (error, []string) {
continue continue
} }
versionStatus := GetVersionStatus(&item) vs := item.versionStatus()
switch versionStatus { switch vs {
case 0: // latest case 0: // latest
if err := CollecDepsCheck(&item); err != nil { if err := CollecDepsCheck(&item); err != nil {
warnings = append(warnings, fmt.Sprintf("dependency of %s: %s", item.Name, err)) warnings = append(warnings, fmt.Sprintf("dependency of %s: %s", item.Name, err))
@ -428,7 +446,7 @@ func SyncDir(hub *csconfig.Hub, dir string) (error, []string) {
warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", item.Name, item.LocalVersion, item.Version)) warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", item.Name, item.LocalVersion, item.Version))
} }
log.Debugf("installed (%s) - status:%d | installed:%s | latest : %s | full : %+v", item.Name, versionStatus, item.LocalVersion, item.Version, item.Versions) log.Debugf("installed (%s) - status:%d | installed:%s | latest : %s | full : %+v", item.Name, vs, item.LocalVersion, item.Version, item.Versions)
} }
return nil, warnings return nil, warnings

View file

@ -56,9 +56,7 @@ func InstallHubItems(csConfig *csconfig.Config, input []byte, dryRun bool) error
return fmt.Errorf("loading hub: %w", err) return fmt.Errorf("loading hub: %w", err)
} }
if err := cwhub.SetHubBranch(); err != nil { cwhub.SetHubBranch()
return fmt.Errorf("setting hub branch: %w", err)
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil { if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
return fmt.Errorf("getting hub index: %w", err) return fmt.Errorf("getting hub index: %w", err)