telemetry.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. Set("domain", d.Domain),
  74. })
  75. }
  76. // FetchTelemetryData - fetches telemetry data: count of various object types in DB
  77. func FetchTelemetryData() telemetryData {
  78. var data telemetryData
  79. data.IsPro = servercfg.IsPro
  80. data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
  81. data.Users = getDBLength(database.USERS_TABLE_NAME)
  82. data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
  83. data.Hosts = getDBLength(database.HOSTS_TABLE_NAME)
  84. data.Version = servercfg.GetVersion()
  85. data.Servers = getServerCount()
  86. nodes, _ := GetAllNodes()
  87. data.Nodes = len(nodes)
  88. data.Count = getClientCount(nodes)
  89. endDate, _ := GetTrialEndDate()
  90. data.ProTrialEndDate = endDate
  91. if endDate.After(time.Now()) {
  92. data.IsProTrial = true
  93. }
  94. data.IsSaasTenant = servercfg.DeployedByOperator()
  95. data.Domain = servercfg.GetNmBaseDomain()
  96. return data
  97. }
  98. // getServerCount returns number of servers from database
  99. func getServerCount() int {
  100. data, err := database.FetchRecords(database.SERVER_UUID_TABLE_NAME)
  101. if err != nil {
  102. logger.Log(0, "error retrieving server data", err.Error())
  103. }
  104. return len(data)
  105. }
  106. // setTelemetryTimestamp - Give the entry in the DB a new timestamp
  107. func setTelemetryTimestamp(telRecord *models.Telemetry) error {
  108. lastsend := time.Now().Unix()
  109. var serverTelData = models.Telemetry{
  110. UUID: telRecord.UUID,
  111. LastSend: lastsend,
  112. TrafficKeyPriv: telRecord.TrafficKeyPriv,
  113. TrafficKeyPub: telRecord.TrafficKeyPub,
  114. }
  115. jsonObj, err := json.Marshal(&serverTelData)
  116. if err != nil {
  117. return err
  118. }
  119. err = database.Insert(database.SERVER_UUID_RECORD_KEY, string(jsonObj), database.SERVER_UUID_TABLE_NAME)
  120. if err == nil {
  121. telServerRecord = serverTelData
  122. }
  123. return err
  124. }
  125. // getClientCount - returns counts of nodes with various OS types and conditions
  126. func getClientCount(nodes []models.Node) clientCount {
  127. var count clientCount
  128. for _, node := range nodes {
  129. host, err := GetHost(node.HostID.String())
  130. if err != nil {
  131. continue
  132. }
  133. switch host.OS {
  134. case "darwin":
  135. count.MacOS += 1
  136. case "windows":
  137. count.Windows += 1
  138. case "linux":
  139. count.Linux += 1
  140. case "freebsd":
  141. count.FreeBSD += 1
  142. }
  143. }
  144. return count
  145. }
  146. // FetchTelemetryRecord - get the existing UUID and Timestamp from the DB
  147. func FetchTelemetryRecord() (models.Telemetry, error) {
  148. if telServerRecord.TrafficKeyPub != nil {
  149. return telServerRecord, nil
  150. }
  151. var rawData string
  152. var telObj models.Telemetry
  153. var err error
  154. rawData, err = database.FetchRecord(database.SERVER_UUID_TABLE_NAME, database.SERVER_UUID_RECORD_KEY)
  155. if err != nil {
  156. return telObj, err
  157. }
  158. err = json.Unmarshal([]byte(rawData), &telObj)
  159. if err == nil {
  160. telServerRecord = telObj
  161. }
  162. return telObj, err
  163. }
  164. // getDBLength - get length of DB to get count of objects
  165. func getDBLength(dbname string) int {
  166. data, err := database.FetchRecords(dbname)
  167. if err != nil {
  168. return 0
  169. }
  170. return len(data)
  171. }
  172. // telemetryData - What data to send to posthog
  173. type telemetryData struct {
  174. Nodes int
  175. Hosts int
  176. ExtClients int
  177. Users int
  178. Count clientCount
  179. Networks int
  180. Servers int
  181. Version string
  182. IsPro bool
  183. IsFreeTier bool
  184. IsProTrial bool
  185. ProTrialEndDate time.Time
  186. IsSaasTenant bool
  187. Domain string
  188. }
  189. // clientCount - What types of netclients we're tallying
  190. type clientCount struct {
  191. MacOS int
  192. Windows int
  193. Linux int
  194. FreeBSD int
  195. K8S int
  196. Docker int
  197. NonServer int
  198. }