telemetry.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. package logic
  2. import (
  3. "encoding/json"
  4. "os"
  5. "time"
  6. "github.com/gravitl/netmaker/database"
  7. "github.com/gravitl/netmaker/logger"
  8. "github.com/gravitl/netmaker/models"
  9. "github.com/gravitl/netmaker/servercfg"
  10. "github.com/posthog/posthog-go"
  11. "golang.org/x/exp/slog"
  12. )
  13. var (
  14. // flags to keep for telemetry
  15. isFreeTier bool
  16. telServerRecord = models.Telemetry{}
  17. )
  18. var LogEvent = func(a *models.Event) {}
  19. // posthog_pub_key - Key for sending data to PostHog
  20. const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
  21. // posthog_endpoint - Endpoint of PostHog server
  22. const posthog_endpoint = "https://app.posthog.com"
  23. // setFreeTierForTelemetry - store free tier flag without having an import cycle when used for telemetry
  24. // (as the pro package needs the logic package as currently written).
  25. func SetFreeTierForTelemetry(freeTierFlag bool) {
  26. isFreeTier = freeTierFlag
  27. }
  28. // sendTelemetry - gathers telemetry data and sends to posthog
  29. func sendTelemetry() error {
  30. if Telemetry() == "off" {
  31. return nil
  32. }
  33. var telRecord, err = FetchTelemetryRecord()
  34. if err != nil {
  35. return err
  36. }
  37. // get telemetry data
  38. d := FetchTelemetryData()
  39. // get tenant admin email
  40. adminEmail := os.Getenv("NM_EMAIL")
  41. client, err := posthog.NewWithConfig(posthog_pub_key, posthog.Config{Endpoint: posthog_endpoint})
  42. if err != nil {
  43. return err
  44. }
  45. defer client.Close()
  46. slog.Info("sending telemetry data to posthog", "data", d)
  47. // send to posthog
  48. return client.Enqueue(posthog.Capture{
  49. DistinctId: telRecord.UUID,
  50. Event: "daily checkin",
  51. Properties: posthog.NewProperties().
  52. Set("nodes", d.Nodes).
  53. Set("hosts", d.Hosts).
  54. Set("servers", d.Servers).
  55. Set("non-server nodes", d.Count.NonServer).
  56. Set("extclients", d.ExtClients).
  57. Set("users", d.Users).
  58. Set("networks", d.Networks).
  59. Set("linux", d.Count.Linux).
  60. Set("darwin", d.Count.MacOS).
  61. Set("windows", d.Count.Windows).
  62. Set("freebsd", d.Count.FreeBSD).
  63. Set("docker", d.Count.Docker).
  64. Set("k8s", d.Count.K8S).
  65. Set("version", d.Version).
  66. Set("is_ee", d.IsPro). // TODO change is_ee to is_pro for consistency, but probably needs changes in posthog
  67. Set("is_free_tier", isFreeTier).
  68. Set("is_pro_trial", d.IsProTrial).
  69. Set("pro_trial_end_date", d.ProTrialEndDate.In(time.UTC).Format("2006-01-02")).
  70. Set("admin_email", adminEmail).
  71. Set("email", adminEmail). // needed for posthog intgration with hubspot. "admin_email" can only be removed if not used in posthog
  72. Set("is_saas_tenant", d.IsSaasTenant),
  73. })
  74. }
  75. // FetchTelemetryData - fetches telemetry data: count of various object types in DB
  76. func FetchTelemetryData() telemetryData {
  77. var data telemetryData
  78. data.IsPro = servercfg.IsPro
  79. data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
  80. data.Users = getDBLength(database.USERS_TABLE_NAME)
  81. data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
  82. data.Hosts = getDBLength(database.HOSTS_TABLE_NAME)
  83. data.Version = servercfg.GetVersion()
  84. data.Servers = getServerCount()
  85. nodes, _ := GetAllNodes()
  86. data.Nodes = len(nodes)
  87. data.Count = getClientCount(nodes)
  88. endDate, _ := GetTrialEndDate()
  89. data.ProTrialEndDate = endDate
  90. if endDate.After(time.Now()) {
  91. data.IsProTrial = true
  92. }
  93. data.IsSaasTenant = servercfg.DeployedByOperator()
  94. return data
  95. }
  96. // getServerCount returns number of servers from database
  97. func getServerCount() int {
  98. data, err := database.FetchRecords(database.SERVER_UUID_TABLE_NAME)
  99. if err != nil {
  100. logger.Log(0, "error retrieving server data", err.Error())
  101. }
  102. return len(data)
  103. }
  104. // setTelemetryTimestamp - Give the entry in the DB a new timestamp
  105. func setTelemetryTimestamp(telRecord *models.Telemetry) error {
  106. lastsend := time.Now().Unix()
  107. var serverTelData = models.Telemetry{
  108. UUID: telRecord.UUID,
  109. LastSend: lastsend,
  110. TrafficKeyPriv: telRecord.TrafficKeyPriv,
  111. TrafficKeyPub: telRecord.TrafficKeyPub,
  112. }
  113. jsonObj, err := json.Marshal(&serverTelData)
  114. if err != nil {
  115. return err
  116. }
  117. err = database.Insert(database.SERVER_UUID_RECORD_KEY, string(jsonObj), database.SERVER_UUID_TABLE_NAME)
  118. if err == nil {
  119. telServerRecord = serverTelData
  120. }
  121. return err
  122. }
  123. // getClientCount - returns counts of nodes with various OS types and conditions
  124. func getClientCount(nodes []models.Node) clientCount {
  125. var count clientCount
  126. for _, node := range nodes {
  127. host, err := GetHost(node.HostID.String())
  128. if err != nil {
  129. continue
  130. }
  131. switch host.OS {
  132. case "darwin":
  133. count.MacOS += 1
  134. case "windows":
  135. count.Windows += 1
  136. case "linux":
  137. count.Linux += 1
  138. case "freebsd":
  139. count.FreeBSD += 1
  140. }
  141. }
  142. return count
  143. }
  144. // FetchTelemetryRecord - get the existing UUID and Timestamp from the DB
  145. func FetchTelemetryRecord() (models.Telemetry, error) {
  146. if telServerRecord.TrafficKeyPub != nil {
  147. return telServerRecord, nil
  148. }
  149. var rawData string
  150. var telObj models.Telemetry
  151. var err error
  152. rawData, err = database.FetchRecord(database.SERVER_UUID_TABLE_NAME, database.SERVER_UUID_RECORD_KEY)
  153. if err != nil {
  154. return telObj, err
  155. }
  156. err = json.Unmarshal([]byte(rawData), &telObj)
  157. if err == nil {
  158. telServerRecord = telObj
  159. }
  160. return telObj, err
  161. }
  162. // getDBLength - get length of DB to get count of objects
  163. func getDBLength(dbname string) int {
  164. data, err := database.FetchRecords(dbname)
  165. if err != nil {
  166. return 0
  167. }
  168. return len(data)
  169. }
  170. // telemetryData - What data to send to posthog
  171. type telemetryData struct {
  172. Nodes int
  173. Hosts int
  174. ExtClients int
  175. Users int
  176. Count clientCount
  177. Networks int
  178. Servers int
  179. Version string
  180. IsPro bool
  181. IsFreeTier bool
  182. IsProTrial bool
  183. ProTrialEndDate time.Time
  184. IsSaasTenant bool
  185. }
  186. // clientCount - What types of netclients we're tallying
  187. type clientCount struct {
  188. MacOS int
  189. Windows int
  190. Linux int
  191. FreeBSD int
  192. K8S int
  193. Docker int
  194. NonServer int
  195. }