utils.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. package main
  2. import (
  3. "encoding/csv"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "math"
  8. "net"
  9. "net/http"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "github.com/fatih/color"
  15. dto "github.com/prometheus/client_model/go"
  16. "github.com/prometheus/prom2json"
  17. log "github.com/sirupsen/logrus"
  18. "github.com/spf13/cobra"
  19. "github.com/texttheater/golang-levenshtein/levenshtein"
  20. "gopkg.in/yaml.v2"
  21. "github.com/crowdsecurity/crowdsec/pkg/cwhub"
  22. "github.com/crowdsecurity/crowdsec/pkg/database"
  23. "github.com/crowdsecurity/crowdsec/pkg/types"
  24. )
  25. const MaxDistance = 7
  26. func printHelp(cmd *cobra.Command) {
  27. err := cmd.Help()
  28. if err != nil {
  29. log.Fatalf("unable to print help(): %s", err)
  30. }
  31. }
  32. func inSlice(s string, slice []string) bool {
  33. for _, str := range slice {
  34. if s == str {
  35. return true
  36. }
  37. }
  38. return false
  39. }
  40. func indexOf(s string, slice []string) int {
  41. for i, elem := range slice {
  42. if s == elem {
  43. return i
  44. }
  45. }
  46. return -1
  47. }
  48. func LoadHub() error {
  49. if err := csConfig.LoadHub(); err != nil {
  50. log.Fatal(err)
  51. }
  52. if csConfig.Hub == nil {
  53. return fmt.Errorf("unable to load hub")
  54. }
  55. if err := cwhub.SetHubBranch(); err != nil {
  56. log.Warningf("unable to set hub branch (%s), default to master", err)
  57. }
  58. if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
  59. return fmt.Errorf("Failed to get Hub index : '%w'. Run 'sudo cscli hub update' to get the hub index", err)
  60. }
  61. return nil
  62. }
  63. func Suggest(itemType string, baseItem string, suggestItem string, score int, ignoreErr bool) {
  64. errMsg := ""
  65. if score < MaxDistance {
  66. errMsg = fmt.Sprintf("unable to find %s '%s', did you mean %s ?", itemType, baseItem, suggestItem)
  67. } else {
  68. errMsg = fmt.Sprintf("unable to find %s '%s'", itemType, baseItem)
  69. }
  70. if ignoreErr {
  71. log.Error(errMsg)
  72. } else {
  73. log.Fatalf(errMsg)
  74. }
  75. }
  76. func GetDistance(itemType string, itemName string) (*cwhub.Item, int) {
  77. allItems := make([]string, 0)
  78. nearestScore := 100
  79. nearestItem := &cwhub.Item{}
  80. hubItems := cwhub.GetHubStatusForItemType(itemType, "", true)
  81. for _, item := range hubItems {
  82. allItems = append(allItems, item.Name)
  83. }
  84. for _, s := range allItems {
  85. d := levenshtein.DistanceForStrings([]rune(itemName), []rune(s), levenshtein.DefaultOptions)
  86. if d < nearestScore {
  87. nearestScore = d
  88. nearestItem = cwhub.GetItem(itemType, s)
  89. }
  90. }
  91. return nearestItem, nearestScore
  92. }
  93. func compAllItems(itemType string, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
  94. if err := LoadHub(); err != nil {
  95. return nil, cobra.ShellCompDirectiveDefault
  96. }
  97. comp := make([]string, 0)
  98. hubItems := cwhub.GetHubStatusForItemType(itemType, "", true)
  99. for _, item := range hubItems {
  100. if !inSlice(item.Name, args) && strings.Contains(item.Name, toComplete) {
  101. comp = append(comp, item.Name)
  102. }
  103. }
  104. cobra.CompDebugln(fmt.Sprintf("%s: %+v", itemType, comp), true)
  105. return comp, cobra.ShellCompDirectiveNoFileComp
  106. }
  107. func compInstalledItems(itemType string, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
  108. if err := LoadHub(); err != nil {
  109. return nil, cobra.ShellCompDirectiveDefault
  110. }
  111. var items []string
  112. var err error
  113. switch itemType {
  114. case cwhub.PARSERS:
  115. items, err = cwhub.GetInstalledParsersAsString()
  116. case cwhub.SCENARIOS:
  117. items, err = cwhub.GetInstalledScenariosAsString()
  118. case cwhub.PARSERS_OVFLW:
  119. items, err = cwhub.GetInstalledPostOverflowsAsString()
  120. case cwhub.COLLECTIONS:
  121. items, err = cwhub.GetInstalledCollectionsAsString()
  122. default:
  123. return nil, cobra.ShellCompDirectiveDefault
  124. }
  125. if err != nil {
  126. cobra.CompDebugln(fmt.Sprintf("list installed %s err: %s", itemType, err), true)
  127. return nil, cobra.ShellCompDirectiveDefault
  128. }
  129. comp := make([]string, 0)
  130. if toComplete != "" {
  131. for _, item := range items {
  132. if strings.Contains(item, toComplete) {
  133. comp = append(comp, item)
  134. }
  135. }
  136. } else {
  137. comp = items
  138. }
  139. cobra.CompDebugln(fmt.Sprintf("%s: %+v", itemType, comp), true)
  140. return comp, cobra.ShellCompDirectiveNoFileComp
  141. }
  142. func ListItems(out io.Writer, itemTypes []string, args []string, showType bool, showHeader bool, all bool) {
  143. var hubStatusByItemType = make(map[string][]cwhub.ItemHubStatus)
  144. for _, itemType := range itemTypes {
  145. itemName := ""
  146. if len(args) == 1 {
  147. itemName = args[0]
  148. }
  149. hubStatusByItemType[itemType] = cwhub.GetHubStatusForItemType(itemType, itemName, all)
  150. }
  151. if csConfig.Cscli.Output == "human" {
  152. for _, itemType := range itemTypes {
  153. var statuses []cwhub.ItemHubStatus
  154. var ok bool
  155. if statuses, ok = hubStatusByItemType[itemType]; !ok {
  156. log.Errorf("unknown item type: %s", itemType)
  157. continue
  158. }
  159. listHubItemTable(out, "\n"+strings.ToUpper(itemType), statuses)
  160. }
  161. } else if csConfig.Cscli.Output == "json" {
  162. x, err := json.MarshalIndent(hubStatusByItemType, "", " ")
  163. if err != nil {
  164. log.Fatalf("failed to unmarshal")
  165. }
  166. out.Write(x)
  167. } else if csConfig.Cscli.Output == "raw" {
  168. csvwriter := csv.NewWriter(out)
  169. if showHeader {
  170. header := []string{"name", "status", "version", "description"}
  171. if showType {
  172. header = append(header, "type")
  173. }
  174. err := csvwriter.Write(header)
  175. if err != nil {
  176. log.Fatalf("failed to write header: %s", err)
  177. }
  178. }
  179. for _, itemType := range itemTypes {
  180. var statuses []cwhub.ItemHubStatus
  181. var ok bool
  182. if statuses, ok = hubStatusByItemType[itemType]; !ok {
  183. log.Errorf("unknown item type: %s", itemType)
  184. continue
  185. }
  186. for _, status := range statuses {
  187. if status.LocalVersion == "" {
  188. status.LocalVersion = "n/a"
  189. }
  190. row := []string{
  191. status.Name,
  192. status.Status,
  193. status.LocalVersion,
  194. status.Description,
  195. }
  196. if showType {
  197. row = append(row, itemType)
  198. }
  199. err := csvwriter.Write(row)
  200. if err != nil {
  201. log.Fatalf("failed to write raw output : %s", err)
  202. }
  203. }
  204. }
  205. csvwriter.Flush()
  206. }
  207. }
  208. func InspectItem(name string, objecitemType string) {
  209. hubItem := cwhub.GetItem(objecitemType, name)
  210. if hubItem == nil {
  211. log.Fatalf("unable to retrieve item.")
  212. }
  213. var b []byte
  214. var err error
  215. switch csConfig.Cscli.Output {
  216. case "human", "raw":
  217. b, err = yaml.Marshal(*hubItem)
  218. if err != nil {
  219. log.Fatalf("unable to marshal item : %s", err)
  220. }
  221. case "json":
  222. b, err = json.MarshalIndent(*hubItem, "", " ")
  223. if err != nil {
  224. log.Fatalf("unable to marshal item : %s", err)
  225. }
  226. }
  227. fmt.Printf("%s", string(b))
  228. if csConfig.Cscli.Output == "json" || csConfig.Cscli.Output == "raw" {
  229. return
  230. }
  231. if prometheusURL == "" {
  232. //This is technically wrong to do this, as the prometheus section contains a listen address, not an URL to query prometheus
  233. //But for ease of use, we will use the listen address as the prometheus URL because it will be 127.0.0.1 in the default case
  234. listenAddr := csConfig.Prometheus.ListenAddr
  235. if listenAddr == "" {
  236. listenAddr = "127.0.0.1"
  237. }
  238. listenPort := csConfig.Prometheus.ListenPort
  239. if listenPort == 0 {
  240. listenPort = 6060
  241. }
  242. prometheusURL = fmt.Sprintf("http://%s:%d/metrics", listenAddr, listenPort)
  243. log.Debugf("No prometheus URL provided using: %s", prometheusURL)
  244. }
  245. fmt.Printf("\nCurrent metrics : \n")
  246. ShowMetrics(hubItem)
  247. }
  248. func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value *string) error {
  249. /*if a range is provided, change the scope*/
  250. if *ipRange != "" {
  251. _, _, err := net.ParseCIDR(*ipRange)
  252. if err != nil {
  253. return fmt.Errorf("%s isn't a valid range", *ipRange)
  254. }
  255. }
  256. if *ip != "" {
  257. ipRepr := net.ParseIP(*ip)
  258. if ipRepr == nil {
  259. return fmt.Errorf("%s isn't a valid ip", *ip)
  260. }
  261. }
  262. //avoid confusion on scope (ip vs Ip and range vs Range)
  263. switch strings.ToLower(*scope) {
  264. case "ip":
  265. *scope = types.Ip
  266. case "range":
  267. *scope = types.Range
  268. case "country":
  269. *scope = types.Country
  270. case "as":
  271. *scope = types.AS
  272. }
  273. return nil
  274. }
  275. func ShowMetrics(hubItem *cwhub.Item) {
  276. switch hubItem.Type {
  277. case cwhub.PARSERS:
  278. metrics := GetParserMetric(prometheusURL, hubItem.Name)
  279. parserMetricsTable(color.Output, hubItem.Name, metrics)
  280. case cwhub.SCENARIOS:
  281. metrics := GetScenarioMetric(prometheusURL, hubItem.Name)
  282. scenarioMetricsTable(color.Output, hubItem.Name, metrics)
  283. case cwhub.COLLECTIONS:
  284. for _, item := range hubItem.Parsers {
  285. metrics := GetParserMetric(prometheusURL, item)
  286. parserMetricsTable(color.Output, item, metrics)
  287. }
  288. for _, item := range hubItem.Scenarios {
  289. metrics := GetScenarioMetric(prometheusURL, item)
  290. scenarioMetricsTable(color.Output, item, metrics)
  291. }
  292. for _, item := range hubItem.Collections {
  293. hubItem = cwhub.GetItem(cwhub.COLLECTIONS, item)
  294. if hubItem == nil {
  295. log.Fatalf("unable to retrieve item '%s' from collection '%s'", item, hubItem.Name)
  296. }
  297. ShowMetrics(hubItem)
  298. }
  299. default:
  300. log.Errorf("item of type '%s' is unknown", hubItem.Type)
  301. }
  302. }
  303. // GetParserMetric is a complete rip from prom2json
  304. func GetParserMetric(url string, itemName string) map[string]map[string]int {
  305. stats := make(map[string]map[string]int)
  306. result := GetPrometheusMetric(url)
  307. for idx, fam := range result {
  308. if !strings.HasPrefix(fam.Name, "cs_") {
  309. continue
  310. }
  311. log.Tracef("round %d", idx)
  312. for _, m := range fam.Metrics {
  313. metric, ok := m.(prom2json.Metric)
  314. if !ok {
  315. log.Debugf("failed to convert metric to prom2json.Metric")
  316. continue
  317. }
  318. name, ok := metric.Labels["name"]
  319. if !ok {
  320. log.Debugf("no name in Metric %v", metric.Labels)
  321. }
  322. if name != itemName {
  323. continue
  324. }
  325. source, ok := metric.Labels["source"]
  326. if !ok {
  327. log.Debugf("no source in Metric %v", metric.Labels)
  328. } else {
  329. if srctype, ok := metric.Labels["type"]; ok {
  330. source = srctype + ":" + source
  331. }
  332. }
  333. value := m.(prom2json.Metric).Value
  334. fval, err := strconv.ParseFloat(value, 32)
  335. if err != nil {
  336. log.Errorf("Unexpected int value %s : %s", value, err)
  337. continue
  338. }
  339. ival := int(fval)
  340. switch fam.Name {
  341. case "cs_reader_hits_total":
  342. if _, ok := stats[source]; !ok {
  343. stats[source] = make(map[string]int)
  344. stats[source]["parsed"] = 0
  345. stats[source]["reads"] = 0
  346. stats[source]["unparsed"] = 0
  347. stats[source]["hits"] = 0
  348. }
  349. stats[source]["reads"] += ival
  350. case "cs_parser_hits_ok_total":
  351. if _, ok := stats[source]; !ok {
  352. stats[source] = make(map[string]int)
  353. }
  354. stats[source]["parsed"] += ival
  355. case "cs_parser_hits_ko_total":
  356. if _, ok := stats[source]; !ok {
  357. stats[source] = make(map[string]int)
  358. }
  359. stats[source]["unparsed"] += ival
  360. case "cs_node_hits_total":
  361. if _, ok := stats[source]; !ok {
  362. stats[source] = make(map[string]int)
  363. }
  364. stats[source]["hits"] += ival
  365. case "cs_node_hits_ok_total":
  366. if _, ok := stats[source]; !ok {
  367. stats[source] = make(map[string]int)
  368. }
  369. stats[source]["parsed"] += ival
  370. case "cs_node_hits_ko_total":
  371. if _, ok := stats[source]; !ok {
  372. stats[source] = make(map[string]int)
  373. }
  374. stats[source]["unparsed"] += ival
  375. default:
  376. continue
  377. }
  378. }
  379. }
  380. return stats
  381. }
  382. func GetScenarioMetric(url string, itemName string) map[string]int {
  383. stats := make(map[string]int)
  384. stats["instantiation"] = 0
  385. stats["curr_count"] = 0
  386. stats["overflow"] = 0
  387. stats["pour"] = 0
  388. stats["underflow"] = 0
  389. result := GetPrometheusMetric(url)
  390. for idx, fam := range result {
  391. if !strings.HasPrefix(fam.Name, "cs_") {
  392. continue
  393. }
  394. log.Tracef("round %d", idx)
  395. for _, m := range fam.Metrics {
  396. metric, ok := m.(prom2json.Metric)
  397. if !ok {
  398. log.Debugf("failed to convert metric to prom2json.Metric")
  399. continue
  400. }
  401. name, ok := metric.Labels["name"]
  402. if !ok {
  403. log.Debugf("no name in Metric %v", metric.Labels)
  404. }
  405. if name != itemName {
  406. continue
  407. }
  408. value := m.(prom2json.Metric).Value
  409. fval, err := strconv.ParseFloat(value, 32)
  410. if err != nil {
  411. log.Errorf("Unexpected int value %s : %s", value, err)
  412. continue
  413. }
  414. ival := int(fval)
  415. switch fam.Name {
  416. case "cs_bucket_created_total":
  417. stats["instantiation"] += ival
  418. case "cs_buckets":
  419. stats["curr_count"] += ival
  420. case "cs_bucket_overflowed_total":
  421. stats["overflow"] += ival
  422. case "cs_bucket_poured_total":
  423. stats["pour"] += ival
  424. case "cs_bucket_underflowed_total":
  425. stats["underflow"] += ival
  426. default:
  427. continue
  428. }
  429. }
  430. }
  431. return stats
  432. }
  433. // it's a rip of the cli version, but in silent-mode
  434. func silenceInstallItem(name string, obtype string) (string, error) {
  435. var item = cwhub.GetItem(obtype, name)
  436. if item == nil {
  437. return "", fmt.Errorf("error retrieving item")
  438. }
  439. it := *item
  440. if downloadOnly && it.Downloaded && it.UpToDate {
  441. return fmt.Sprintf("%s is already downloaded and up-to-date", it.Name), nil
  442. }
  443. it, err := cwhub.DownloadLatest(csConfig.Hub, it, forceAction, false)
  444. if err != nil {
  445. return "", fmt.Errorf("error while downloading %s : %v", it.Name, err)
  446. }
  447. if err := cwhub.AddItem(obtype, it); err != nil {
  448. return "", err
  449. }
  450. if downloadOnly {
  451. return fmt.Sprintf("Downloaded %s to %s", it.Name, csConfig.Cscli.HubDir+"/"+it.RemotePath), nil
  452. }
  453. it, err = cwhub.EnableItem(csConfig.Hub, it)
  454. if err != nil {
  455. return "", fmt.Errorf("error while enabling %s : %v", it.Name, err)
  456. }
  457. if err := cwhub.AddItem(obtype, it); err != nil {
  458. return "", err
  459. }
  460. return fmt.Sprintf("Enabled %s", it.Name), nil
  461. }
  462. func GetPrometheusMetric(url string) []*prom2json.Family {
  463. mfChan := make(chan *dto.MetricFamily, 1024)
  464. // Start with the DefaultTransport for sane defaults.
  465. transport := http.DefaultTransport.(*http.Transport).Clone()
  466. // Conservatively disable HTTP keep-alives as this program will only
  467. // ever need a single HTTP request.
  468. transport.DisableKeepAlives = true
  469. // Timeout early if the server doesn't even return the headers.
  470. transport.ResponseHeaderTimeout = time.Minute
  471. go func() {
  472. defer types.CatchPanic("crowdsec/GetPrometheusMetric")
  473. err := prom2json.FetchMetricFamilies(url, mfChan, transport)
  474. if err != nil {
  475. log.Fatalf("failed to fetch prometheus metrics : %v", err)
  476. }
  477. }()
  478. result := []*prom2json.Family{}
  479. for mf := range mfChan {
  480. result = append(result, prom2json.NewFamily(mf))
  481. }
  482. log.Debugf("Finished reading prometheus output, %d entries", len(result))
  483. return result
  484. }
  485. func RestoreHub(dirPath string) error {
  486. var err error
  487. if err := csConfig.LoadHub(); err != nil {
  488. return err
  489. }
  490. if err := cwhub.SetHubBranch(); err != nil {
  491. return fmt.Errorf("error while setting hub branch: %s", err)
  492. }
  493. for _, itype := range cwhub.ItemTypes {
  494. itemDirectory := fmt.Sprintf("%s/%s/", dirPath, itype)
  495. if _, err = os.Stat(itemDirectory); err != nil {
  496. log.Infof("no %s in backup", itype)
  497. continue
  498. }
  499. /*restore the upstream items*/
  500. upstreamListFN := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itype)
  501. file, err := os.ReadFile(upstreamListFN)
  502. if err != nil {
  503. return fmt.Errorf("error while opening %s : %s", upstreamListFN, err)
  504. }
  505. var upstreamList []string
  506. err = json.Unmarshal(file, &upstreamList)
  507. if err != nil {
  508. return fmt.Errorf("error unmarshaling %s : %s", upstreamListFN, err)
  509. }
  510. for _, toinstall := range upstreamList {
  511. label, err := silenceInstallItem(toinstall, itype)
  512. if err != nil {
  513. log.Errorf("Error while installing %s : %s", toinstall, err)
  514. } else if label != "" {
  515. log.Infof("Installed %s : %s", toinstall, label)
  516. } else {
  517. log.Printf("Installed %s : ok", toinstall)
  518. }
  519. }
  520. /*restore the local and tainted items*/
  521. files, err := os.ReadDir(itemDirectory)
  522. if err != nil {
  523. return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory, err)
  524. }
  525. for _, file := range files {
  526. //this was the upstream data
  527. if file.Name() == fmt.Sprintf("upstream-%s.json", itype) {
  528. continue
  529. }
  530. if itype == cwhub.PARSERS || itype == cwhub.PARSERS_OVFLW {
  531. //we expect a stage here
  532. if !file.IsDir() {
  533. continue
  534. }
  535. stage := file.Name()
  536. stagedir := fmt.Sprintf("%s/%s/%s/", csConfig.ConfigPaths.ConfigDir, itype, stage)
  537. log.Debugf("Found stage %s in %s, target directory : %s", stage, itype, stagedir)
  538. if err = os.MkdirAll(stagedir, os.ModePerm); err != nil {
  539. return fmt.Errorf("error while creating stage directory %s : %s", stagedir, err)
  540. }
  541. /*find items*/
  542. ifiles, err := os.ReadDir(itemDirectory + "/" + stage + "/")
  543. if err != nil {
  544. return fmt.Errorf("failed enumerating files of %s : %s", itemDirectory+"/"+stage, err)
  545. }
  546. //finally copy item
  547. for _, tfile := range ifiles {
  548. log.Infof("Going to restore local/tainted [%s]", tfile.Name())
  549. sourceFile := fmt.Sprintf("%s/%s/%s", itemDirectory, stage, tfile.Name())
  550. destinationFile := fmt.Sprintf("%s%s", stagedir, tfile.Name())
  551. if err = types.CopyFile(sourceFile, destinationFile); err != nil {
  552. return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
  553. }
  554. log.Infof("restored %s to %s", sourceFile, destinationFile)
  555. }
  556. } else {
  557. log.Infof("Going to restore local/tainted [%s]", file.Name())
  558. sourceFile := fmt.Sprintf("%s/%s", itemDirectory, file.Name())
  559. destinationFile := fmt.Sprintf("%s/%s/%s", csConfig.ConfigPaths.ConfigDir, itype, file.Name())
  560. if err = types.CopyFile(sourceFile, destinationFile); err != nil {
  561. return fmt.Errorf("failed copy %s %s to %s : %s", itype, sourceFile, destinationFile, err)
  562. }
  563. log.Infof("restored %s to %s", sourceFile, destinationFile)
  564. }
  565. }
  566. }
  567. return nil
  568. }
  569. func BackupHub(dirPath string) error {
  570. var err error
  571. var itemDirectory string
  572. var upstreamParsers []string
  573. for _, itemType := range cwhub.ItemTypes {
  574. clog := log.WithFields(log.Fields{
  575. "type": itemType,
  576. })
  577. itemMap := cwhub.GetItemMap(itemType)
  578. if itemMap == nil {
  579. clog.Infof("No %s to backup.", itemType)
  580. continue
  581. }
  582. itemDirectory = fmt.Sprintf("%s/%s/", dirPath, itemType)
  583. if err := os.MkdirAll(itemDirectory, os.ModePerm); err != nil {
  584. return fmt.Errorf("error while creating %s : %s", itemDirectory, err)
  585. }
  586. upstreamParsers = []string{}
  587. for k, v := range itemMap {
  588. clog = clog.WithFields(log.Fields{
  589. "file": v.Name,
  590. })
  591. if !v.Installed { //only backup installed ones
  592. clog.Debugf("[%s] : not installed", k)
  593. continue
  594. }
  595. //for the local/tainted ones, we backup the full file
  596. if v.Tainted || v.Local || !v.UpToDate {
  597. //we need to backup stages for parsers
  598. if itemType == cwhub.PARSERS || itemType == cwhub.PARSERS_OVFLW {
  599. fstagedir := fmt.Sprintf("%s%s", itemDirectory, v.Stage)
  600. if err := os.MkdirAll(fstagedir, os.ModePerm); err != nil {
  601. return fmt.Errorf("error while creating stage dir %s : %s", fstagedir, err)
  602. }
  603. }
  604. clog.Debugf("[%s] : backuping file (tainted:%t local:%t up-to-date:%t)", k, v.Tainted, v.Local, v.UpToDate)
  605. tfile := fmt.Sprintf("%s%s/%s", itemDirectory, v.Stage, v.FileName)
  606. if err = types.CopyFile(v.LocalPath, tfile); err != nil {
  607. return fmt.Errorf("failed copy %s %s to %s : %s", itemType, v.LocalPath, tfile, err)
  608. }
  609. clog.Infof("local/tainted saved %s to %s", v.LocalPath, tfile)
  610. continue
  611. }
  612. clog.Debugf("[%s] : from hub, just backup name (up-to-date:%t)", k, v.UpToDate)
  613. clog.Infof("saving, version:%s, up-to-date:%t", v.Version, v.UpToDate)
  614. upstreamParsers = append(upstreamParsers, v.Name)
  615. }
  616. //write the upstream items
  617. upstreamParsersFname := fmt.Sprintf("%s/upstream-%s.json", itemDirectory, itemType)
  618. upstreamParsersContent, err := json.MarshalIndent(upstreamParsers, "", " ")
  619. if err != nil {
  620. return fmt.Errorf("failed marshaling upstream parsers : %s", err)
  621. }
  622. err = os.WriteFile(upstreamParsersFname, upstreamParsersContent, 0644)
  623. if err != nil {
  624. return fmt.Errorf("unable to write to %s %s : %s", itemType, upstreamParsersFname, err)
  625. }
  626. clog.Infof("Wrote %d entries for %s to %s", len(upstreamParsers), itemType, upstreamParsersFname)
  627. }
  628. return nil
  629. }
  630. type unit struct {
  631. value int64
  632. symbol string
  633. }
  634. var ranges = []unit{
  635. {
  636. value: 1e18,
  637. symbol: "E",
  638. },
  639. {
  640. value: 1e15,
  641. symbol: "P",
  642. },
  643. {
  644. value: 1e12,
  645. symbol: "T",
  646. },
  647. {
  648. value: 1e6,
  649. symbol: "M",
  650. },
  651. {
  652. value: 1e3,
  653. symbol: "k",
  654. },
  655. {
  656. value: 1,
  657. symbol: "",
  658. },
  659. }
  660. func formatNumber(num int) string {
  661. goodUnit := unit{}
  662. for _, u := range ranges {
  663. if int64(num) >= u.value {
  664. goodUnit = u
  665. break
  666. }
  667. }
  668. if goodUnit.value == 1 {
  669. return fmt.Sprintf("%d%s", num, goodUnit.symbol)
  670. }
  671. res := math.Round(float64(num)/float64(goodUnit.value)*100) / 100
  672. return fmt.Sprintf("%.2f%s", res, goodUnit.symbol)
  673. }
  674. func getDBClient() (*database.Client, error) {
  675. var err error
  676. if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
  677. return nil, err
  678. }
  679. ret, err := database.NewClient(csConfig.DbConfig)
  680. if err != nil {
  681. return nil, err
  682. }
  683. return ret, nil
  684. }
  685. func removeFromSlice(val string, slice []string) []string {
  686. var i int
  687. var value string
  688. valueFound := false
  689. // get the index
  690. for i, value = range slice {
  691. if value == val {
  692. valueFound = true
  693. break
  694. }
  695. }
  696. if valueFound {
  697. slice[i] = slice[len(slice)-1]
  698. slice[len(slice)-1] = ""
  699. slice = slice[:len(slice)-1]
  700. }
  701. return slice
  702. }