usage.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package usage
  2. import (
  3. "time"
  4. "github.com/porter-dev/porter/api/server/shared/config/env"
  5. "github.com/porter-dev/porter/api/types"
  6. "github.com/porter-dev/porter/internal/adapter"
  7. "github.com/porter-dev/porter/internal/models"
  8. "github.com/porter-dev/porter/internal/oauth"
  9. "github.com/porter-dev/porter/internal/repository"
  10. "github.com/porter-dev/porter/internal/usage"
  11. "golang.org/x/oauth2"
  12. "gorm.io/gorm"
  13. rgorm "github.com/porter-dev/porter/internal/repository/gorm"
  14. )
  15. type UsageTracker struct {
  16. db *gorm.DB
  17. repo repository.Repository
  18. doConf *oauth2.Config
  19. whitelistedUsers map[uint]uint
  20. }
  21. type UsageTrackerOpts struct {
  22. DBConf *env.DBConf
  23. DOClientID string
  24. DOClientSecret string
  25. DOScopes []string
  26. ServerURL string
  27. WhitelistedUsers map[uint]uint
  28. }
  29. const stepSize = 100
  30. func NewUsageTracker(opts *UsageTrackerOpts) (*UsageTracker, error) {
  31. db, err := adapter.New(opts.DBConf)
  32. if err != nil {
  33. return nil, err
  34. }
  35. var key [32]byte
  36. for i, b := range []byte(opts.DBConf.EncryptionKey) {
  37. key[i] = b
  38. }
  39. repo := rgorm.NewRepository(db, &key)
  40. doConf := oauth.NewDigitalOceanClient(&oauth.Config{
  41. ClientID: opts.DOClientID,
  42. ClientSecret: opts.DOClientSecret,
  43. Scopes: opts.DOScopes,
  44. BaseURL: opts.ServerURL,
  45. })
  46. return &UsageTracker{db, repo, doConf, opts.WhitelistedUsers}, nil
  47. }
  48. type UsageTrackerResponse struct {
  49. CPULimit uint
  50. CPUUsage uint
  51. MemoryLimit uint
  52. MemoryUsage uint
  53. UserLimit uint
  54. UserUsage uint
  55. ClusterLimit uint
  56. ClusterUsage uint
  57. Exceeded bool
  58. ExceededSince time.Time
  59. Project models.Project
  60. AdminEmails []string
  61. }
  62. func (u *UsageTracker) GetProjectUsage() (map[uint]*UsageTrackerResponse, error) {
  63. res := make(map[uint]*UsageTrackerResponse)
  64. // get the count of the projects
  65. var count int64
  66. if err := u.db.Model(&models.Project{}).Count(&count).Error; err != nil {
  67. return nil, err
  68. }
  69. // iterate (count / stepSize) + 1 times using Limit and Offset
  70. for i := 0; i < (int(count)/stepSize)+1; i++ {
  71. projects := []*models.Project{}
  72. if err := u.db.Order("id asc").Offset(i * stepSize).Limit(stepSize).Find(&projects).Error; err != nil {
  73. return nil, err
  74. }
  75. // go through each project
  76. for _, project := range projects {
  77. current, limit, cache, err := usage.GetUsage(&usage.GetUsageOpts{
  78. Repo: u.repo,
  79. DOConf: u.doConf,
  80. Project: project,
  81. WhitelistedUsers: u.whitelistedUsers,
  82. })
  83. if err != nil {
  84. continue
  85. }
  86. // get the admin emails for the project
  87. roles, err := u.repo.Project().ListProjectRoles(project.ID)
  88. if err != nil {
  89. continue
  90. }
  91. adminEmails := make([]string, 0)
  92. for _, role := range roles {
  93. if role.Kind == types.RoleAdmin {
  94. user, err := u.repo.User().ReadUser(role.UserID)
  95. if err != nil {
  96. continue
  97. }
  98. adminEmails = append(adminEmails, user.Email)
  99. }
  100. }
  101. exceededSince := cache.ExceededSince
  102. if exceededSince == nil {
  103. now := time.Now()
  104. exceededSince = &now
  105. }
  106. res[project.ID] = &UsageTrackerResponse{
  107. CPUUsage: cache.ResourceCPU,
  108. CPULimit: limit.ResourceCPU,
  109. MemoryUsage: cache.ResourceMemory,
  110. MemoryLimit: limit.ResourceMemory,
  111. UserUsage: current.Users,
  112. UserLimit: limit.Users,
  113. ClusterUsage: current.Clusters,
  114. ClusterLimit: limit.Clusters,
  115. Exceeded: cache.Exceeded,
  116. ExceededSince: *exceededSince,
  117. Project: *project,
  118. AdminEmails: adminEmails,
  119. }
  120. }
  121. }
  122. return res, nil
  123. }