usage.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. Exceeded bool
  54. ExceededSince *time.Time
  55. Project *models.Project
  56. AdminEmails []string
  57. }
  58. func (u *UsageTracker) GetProjectUsage() (map[uint]*UsageTrackerResponse, error) {
  59. res := make(map[uint]*UsageTrackerResponse)
  60. // get the count of the projects
  61. var count int64
  62. if err := u.db.Model(&models.Project{}).Count(&count).Error; err != nil {
  63. return nil, err
  64. }
  65. // iterate (count / stepSize) + 1 times using Limit and Offset
  66. for i := 0; i < (int(count)/stepSize)+1; i++ {
  67. projects := []*models.Project{}
  68. if err := u.db.Order("id asc").Offset(i * stepSize).Limit(stepSize).Find(&projects).Error; err != nil {
  69. return nil, err
  70. }
  71. // go through each project
  72. for _, project := range projects {
  73. _, limit, cache, err := usage.GetUsage(&usage.GetUsageOpts{
  74. Repo: u.repo,
  75. DOConf: u.doConf,
  76. Project: project,
  77. WhitelistedUsers: u.whitelistedUsers,
  78. })
  79. if err != nil {
  80. continue
  81. }
  82. // get the admin emails for the project
  83. roles, err := u.repo.Project().ListProjectRoles(project.ID)
  84. if err != nil {
  85. continue
  86. }
  87. adminEmails := make([]string, 0)
  88. for _, role := range roles {
  89. if role.Kind == types.RoleAdmin {
  90. user, err := u.repo.User().ReadUser(role.UserID)
  91. if err != nil {
  92. continue
  93. }
  94. adminEmails = append(adminEmails, user.Email)
  95. }
  96. }
  97. res[project.ID] = &UsageTrackerResponse{
  98. CPUUsage: cache.ResourceCPU,
  99. CPULimit: limit.ResourceCPU,
  100. MemoryUsage: cache.ResourceMemory,
  101. MemoryLimit: limit.ResourceMemory,
  102. Exceeded: cache.Exceeded,
  103. ExceededSince: cache.ExceededSince,
  104. Project: project,
  105. AdminEmails: adminEmails,
  106. }
  107. }
  108. }
  109. return res, nil
  110. }