updateCheck.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package updatecheck
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "github.com/go-co-op/gocron"
  7. "github.com/google/uuid"
  8. config "github.com/jamesread/OliveTin/internal/config"
  9. log "github.com/sirupsen/logrus"
  10. "io/ioutil"
  11. "net/http"
  12. "os"
  13. "runtime"
  14. "time"
  15. )
  16. type updateRequest struct {
  17. CurrentVersion string
  18. CurrentCommit string
  19. OS string
  20. Arch string
  21. InstallationID string
  22. InContainer bool
  23. }
  24. // AvailableVersion is updated when checking with the update service.
  25. var AvailableVersion = "none"
  26. // CurrentVersion is set by the main cmd (which is in tern set as a compile constant)
  27. var CurrentVersion = "?"
  28. func getInstanceIDFilename() string {
  29. directory := "./"
  30. if _, err := os.Stat("/etc/OliveTin"); !os.IsNotExist(err) {
  31. directory = "/etc/OliveTin/"
  32. }
  33. if _, err := os.Stat("/config"); !os.IsNotExist(err) {
  34. directory = "/config/"
  35. }
  36. return directory + "installation-id.txt"
  37. }
  38. func installationID() string {
  39. filename := getInstanceIDFilename()
  40. content := "unset"
  41. contentBytes, err := ioutil.ReadFile(filename)
  42. if err != nil {
  43. fileHandle, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
  44. if err != nil {
  45. log.Warnf("Could not read + create installation ID file: %v", err)
  46. return "cant-create"
  47. }
  48. content = uuid.NewString()
  49. fileHandle.WriteString(content)
  50. fileHandle.Close()
  51. } else {
  52. content = string(contentBytes)
  53. }
  54. log.WithFields(log.Fields{
  55. "content": content,
  56. }).Infof("Installation ID")
  57. return content
  58. }
  59. func isInContainer() bool {
  60. if _, err := os.Stat("/.dockerenv"); errors.Is(err, os.ErrNotExist) {
  61. return false
  62. }
  63. return true
  64. }
  65. // StartUpdateChecker will start a job that runs periodically, checking
  66. // for updates.
  67. func StartUpdateChecker(currentVersion string, currentCommit string, cfg *config.Config) {
  68. CurrentVersion = currentVersion
  69. if !cfg.CheckForUpdates {
  70. log.Warn("Update checking is disabled")
  71. return
  72. }
  73. payload := updateRequest{
  74. CurrentVersion: currentVersion,
  75. CurrentCommit: currentCommit,
  76. OS: runtime.GOOS,
  77. Arch: runtime.GOARCH,
  78. InstallationID: installationID(),
  79. InContainer: isInContainer(),
  80. }
  81. s := gocron.NewScheduler(time.UTC)
  82. s.Every(7).Days().Do(func() {
  83. actualCheckForUpdate(payload)
  84. })
  85. s.StartAsync()
  86. }
  87. func doRequest(jsonUpdateRequest []byte) string {
  88. req, err := http.NewRequest("POST", "http://update-check.olivetin.app", bytes.NewBuffer(jsonUpdateRequest))
  89. if err != nil {
  90. log.Errorf("Update check failed %v", err)
  91. return ""
  92. }
  93. req.Header.Set("Content-Type", "application/json")
  94. resp, err := http.DefaultClient.Do(req)
  95. if err != nil {
  96. log.Errorf("Update check failed %v", err)
  97. return ""
  98. }
  99. newVersion, _ := ioutil.ReadAll(resp.Body)
  100. defer resp.Body.Close()
  101. return string(newVersion)
  102. }
  103. func actualCheckForUpdate(payload updateRequest) {
  104. jsonUpdateRequest, err := json.Marshal(payload)
  105. log.Debugf("Update request payload: %+v", payload)
  106. if err != nil {
  107. log.Errorf("Update check failed %v", err)
  108. return
  109. }
  110. AvailableVersion = doRequest(jsonUpdateRequest)
  111. log.WithFields(log.Fields{
  112. "NewVersion": AvailableVersion,
  113. }).Infof("Update check complete")
  114. }