2
0

usage.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package usage
  2. import (
  3. "errors"
  4. "fmt"
  5. "time"
  6. "github.com/porter-dev/porter/api/server/authz"
  7. "github.com/porter-dev/porter/api/server/shared/config"
  8. "github.com/porter-dev/porter/api/types"
  9. "github.com/porter-dev/porter/internal/kubernetes"
  10. "github.com/porter-dev/porter/internal/kubernetes/nodes"
  11. "github.com/porter-dev/porter/internal/models"
  12. "gorm.io/gorm"
  13. )
  14. // GetUsage gets a project's current usage and usage limit
  15. func GetUsage(config *config.Config, proj *models.Project) (
  16. current, limit *types.ProjectUsage,
  17. resourceUse *models.ProjectUsageCache,
  18. err error,
  19. ) {
  20. limit, err = GetLimit(config, proj)
  21. if err != nil {
  22. return nil, nil, nil, err
  23. }
  24. // query for the linked cluster counts
  25. clusters, err := config.Repo.Cluster().ListClustersByProjectID(proj.ID)
  26. if err != nil {
  27. return nil, nil, nil, err
  28. }
  29. // query for the linked user counts
  30. roles, err := config.Repo.Project().ListProjectRoles(proj.ID)
  31. if err != nil {
  32. return nil, nil, nil, err
  33. }
  34. usageCache, err := config.Repo.ProjectUsage().ReadProjectUsageCache(proj.ID)
  35. isCacheFound := true
  36. if isCacheFound = !errors.Is(err, gorm.ErrRecordNotFound); err != nil && isCacheFound {
  37. return nil, nil, nil, err
  38. }
  39. // if the usage cache is 24 hours old, was not found, or usage is over limit,
  40. // re-query for the usage
  41. if !isCacheFound || usageCache.Is24HrOld() || usageCache.ResourceMemory > limit.ResourceMemory || usageCache.ResourceCPU > limit.ResourceCPU {
  42. cpu, memory, err := getResourceUsage(config, clusters)
  43. if err != nil {
  44. return nil, nil, nil, err
  45. }
  46. if !isCacheFound {
  47. usageCache = &models.ProjectUsageCache{
  48. ProjectID: proj.ID,
  49. ResourceCPU: cpu,
  50. ResourceMemory: memory,
  51. }
  52. } else {
  53. usageCache.ResourceCPU = cpu
  54. usageCache.ResourceMemory = memory
  55. }
  56. isExceeded := usageCache.ResourceCPU > limit.ResourceCPU || usageCache.ResourceMemory > limit.ResourceMemory
  57. if !usageCache.Exceeded && isExceeded {
  58. // update the usage cache with a time exceeded
  59. currTime := time.Now()
  60. usageCache.ExceededSince = &currTime
  61. }
  62. usageCache.Exceeded = isExceeded
  63. if !isCacheFound {
  64. usageCache, err = config.Repo.ProjectUsage().CreateProjectUsageCache(usageCache)
  65. } else {
  66. usageCache, err = config.Repo.ProjectUsage().UpdateProjectUsageCache(usageCache)
  67. }
  68. }
  69. return &types.ProjectUsage{
  70. ResourceCPU: usageCache.ResourceCPU,
  71. ResourceMemory: usageCache.ResourceMemory,
  72. Clusters: uint(len(clusters)),
  73. Users: uint(len(roles)),
  74. }, limit, usageCache, nil
  75. }
  76. // gets the total resource usage across all nodes in all clusters
  77. func getResourceUsage(config *config.Config, clusters []*models.Cluster) (uint, uint, error) {
  78. // TODO; pass this in?
  79. var totCPU, totMem uint = 0, 0
  80. getter := authz.NewOutOfClusterAgentGetter(config)
  81. for _, cluster := range clusters {
  82. ooc := getter.GetOutOfClusterConfig(cluster)
  83. agent, err := kubernetes.GetAgentOutOfClusterConfig(ooc)
  84. if err != nil {
  85. return 0, 0, fmt.Errorf("failed to get agent: %s", err.Error())
  86. }
  87. totAlloc, err := nodes.GetAllocatableResources(agent.Clientset)
  88. if err != nil {
  89. return 0, 0, fmt.Errorf("failed to get alloc: %s", err.Error())
  90. }
  91. totCPU += totAlloc.CPU
  92. totMem += totAlloc.Memory
  93. }
  94. return totCPU / 1000, totMem / (1024 * 1024), nil
  95. }