telemetry.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. // flags to keep for telemetry
  14. var isFreeTier bool
  15. // posthog_pub_key - Key for sending data to PostHog
  16. const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
  17. // posthog_endpoint - Endpoint of PostHog server
  18. const posthog_endpoint = "https://app.posthog.com"
  19. // setFreeTierForTelemetry - store free tier flag without having an import cycle when used for telemetry
  20. // (as the pro package needs the logic package as currently written).
  21. func SetFreeTierForTelemetry(freeTierFlag bool) {
  22. isFreeTier = freeTierFlag
  23. }
  24. // sendTelemetry - gathers telemetry data and sends to posthog
  25. func sendTelemetry() error {
  26. if servercfg.Telemetry() == "off" {
  27. return nil
  28. }
  29. var telRecord, err = FetchTelemetryRecord()
  30. if err != nil {
  31. return err
  32. }
  33. // get telemetry data
  34. d, err := FetchTelemetryData()
  35. if err != nil {
  36. slog.Error("error fetching telemetry data", "error", err)
  37. }
  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("is_saas_tenant", d.IsSaasTenant),
  71. })
  72. }
  73. // FetchTelemetryData - fetches telemetry data: count of various object types in DB
  74. func FetchTelemetryData() (telemetryData, error) {
  75. var data telemetryData
  76. data.IsPro = servercfg.IsPro
  77. data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
  78. data.Users = getDBLength(database.USERS_TABLE_NAME)
  79. data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
  80. data.Hosts = getDBLength(database.HOSTS_TABLE_NAME)
  81. data.Version = servercfg.GetVersion()
  82. data.Servers = getServerCount()
  83. nodes, err := GetAllNodes()
  84. if err == nil {
  85. data.Nodes = len(nodes)
  86. data.Count = getClientCount(nodes)
  87. }
  88. endDate, err := GetTrialEndDate()
  89. if err != nil {
  90. logger.Log(0, "error getting trial end date", err.Error())
  91. }
  92. data.ProTrialEndDate = endDate
  93. if endDate.After(time.Now()) {
  94. data.IsProTrial = true
  95. }
  96. data.IsSaasTenant = servercfg.DeployedByOperator()
  97. return data, err
  98. }
  99. // getServerCount returns number of servers from database
  100. func getServerCount() int {
  101. data, err := database.FetchRecords(database.SERVER_UUID_TABLE_NAME)
  102. if err != nil {
  103. logger.Log(0, "errror retrieving server data", err.Error())
  104. }
  105. return len(data)
  106. }
  107. // setTelemetryTimestamp - Give the entry in the DB a new timestamp
  108. func setTelemetryTimestamp(telRecord *models.Telemetry) error {
  109. lastsend := time.Now().Unix()
  110. var serverTelData = models.Telemetry{
  111. UUID: telRecord.UUID,
  112. LastSend: lastsend,
  113. TrafficKeyPriv: telRecord.TrafficKeyPriv,
  114. TrafficKeyPub: telRecord.TrafficKeyPub,
  115. }
  116. jsonObj, err := json.Marshal(&serverTelData)
  117. if err != nil {
  118. return err
  119. }
  120. err = database.Insert(database.SERVER_UUID_RECORD_KEY, string(jsonObj), database.SERVER_UUID_TABLE_NAME)
  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. var rawData string
  147. var telObj models.Telemetry
  148. var err error
  149. rawData, err = database.FetchRecord(database.SERVER_UUID_TABLE_NAME, database.SERVER_UUID_RECORD_KEY)
  150. if err != nil {
  151. return telObj, err
  152. }
  153. err = json.Unmarshal([]byte(rawData), &telObj)
  154. return telObj, err
  155. }
  156. // getDBLength - get length of DB to get count of objects
  157. func getDBLength(dbname string) int {
  158. data, err := database.FetchRecords(dbname)
  159. if err != nil {
  160. return 0
  161. }
  162. return len(data)
  163. }
  164. // telemetryData - What data to send to posthog
  165. type telemetryData struct {
  166. Nodes int
  167. Hosts int
  168. ExtClients int
  169. Users int
  170. Count clientCount
  171. Networks int
  172. Servers int
  173. Version string
  174. IsPro bool
  175. IsFreeTier bool
  176. IsProTrial bool
  177. ProTrialEndDate time.Time
  178. IsSaasTenant bool
  179. }
  180. // clientCount - What types of netclients we're tallying
  181. type clientCount struct {
  182. MacOS int
  183. Windows int
  184. Linux int
  185. FreeBSD int
  186. K8S int
  187. Docker int
  188. NonServer int
  189. }