usage.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. }
  20. type UsageTrackerOpts struct {
  21. DBConf *env.DBConf
  22. DOClientID string
  23. DOClientSecret string
  24. DOScopes []string
  25. ServerURL string
  26. }
  27. const stepSize = 100
  28. func NewUsageTracker(opts *UsageTrackerOpts) (*UsageTracker, error) {
  29. db, err := adapter.New(opts.DBConf)
  30. if err != nil {
  31. return nil, err
  32. }
  33. var key [32]byte
  34. for i, b := range []byte(opts.DBConf.EncryptionKey) {
  35. key[i] = b
  36. }
  37. repo := rgorm.NewRepository(db, &key)
  38. doConf := oauth.NewDigitalOceanClient(&oauth.Config{
  39. ClientID: opts.DOClientID,
  40. ClientSecret: opts.DOClientSecret,
  41. Scopes: opts.DOScopes,
  42. BaseURL: opts.ServerURL,
  43. })
  44. return &UsageTracker{db, repo, doConf}, nil
  45. }
  46. type UsageTrackerResponse struct {
  47. CPULimit uint
  48. CPUUsage uint
  49. MemoryLimit uint
  50. MemoryUsage uint
  51. Exceeded bool
  52. ExceededSince *time.Time
  53. Project *models.Project
  54. AdminEmails []string
  55. }
  56. func (u *UsageTracker) GetProjectUsage() (map[uint]*UsageTrackerResponse, error) {
  57. res := make(map[uint]*UsageTrackerResponse)
  58. // get the count of the projects
  59. var count int64
  60. if err := u.db.Model(&models.Project{}).Count(&count).Error; err != nil {
  61. return nil, err
  62. }
  63. // iterate (count / stepSize) + 1 times using Limit and Offset
  64. for i := 0; i < (int(count)/stepSize)+1; i++ {
  65. projects := []*models.Project{}
  66. if err := u.db.Order("id asc").Offset(i * stepSize).Limit(stepSize).Find(&projects).Error; err != nil {
  67. return nil, err
  68. }
  69. // go through each project
  70. for _, project := range projects {
  71. _, limit, cache, err := usage.GetUsage(&usage.GetUsageOpts{
  72. Repo: u.repo,
  73. DOConf: u.doConf,
  74. Project: project,
  75. })
  76. if err != nil {
  77. continue
  78. }
  79. // get the admin emails for the project
  80. roles, err := u.repo.Project().ListProjectRoles(project.ID)
  81. if err != nil {
  82. continue
  83. }
  84. adminEmails := make([]string, 0)
  85. for _, role := range roles {
  86. if role.Kind == types.RoleAdmin {
  87. user, err := u.repo.User().ReadUser(role.UserID)
  88. if err != nil {
  89. continue
  90. }
  91. adminEmails = append(adminEmails, user.Email)
  92. }
  93. }
  94. res[project.ID] = &UsageTrackerResponse{
  95. CPUUsage: cache.ResourceCPU,
  96. CPULimit: limit.ResourceCPU,
  97. MemoryUsage: cache.ResourceMemory,
  98. MemoryLimit: limit.ResourceMemory,
  99. Exceeded: cache.Exceeded,
  100. ExceededSince: cache.ExceededSince,
  101. Project: project,
  102. AdminEmails: adminEmails,
  103. }
  104. }
  105. }
  106. return res, nil
  107. }