telemetry.go 5.8 KB

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