utils.go 21 KB

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