utils.go 20 KB

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