usage.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package middleware
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/porter-dev/porter/api/server/handlers/project"
  6. "github.com/porter-dev/porter/api/server/shared/apierrors"
  7. "github.com/porter-dev/porter/api/server/shared/config"
  8. "github.com/porter-dev/porter/api/types"
  9. )
  10. type UsageMiddleware struct {
  11. config *config.Config
  12. metric types.UsageMetric
  13. }
  14. func NewUsageMiddleware(config *config.Config, metric types.UsageMetric) *UsageMiddleware {
  15. return &UsageMiddleware{config, metric}
  16. }
  17. var UsageErrFmt = "usage limit reached for metric %s: limit %d, requested %d"
  18. func (b *UsageMiddleware) Middleware(next http.Handler) http.Handler {
  19. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  20. // get the project usage limits
  21. currentUsage, limit, err := project.GetUsage(b.config, r)
  22. if err != nil {
  23. apierrors.HandleAPIError(
  24. b.config,
  25. w, r,
  26. apierrors.NewErrInternal(err),
  27. true,
  28. )
  29. return
  30. }
  31. // check the usage limits
  32. allowed := allowUsage(limit, currentUsage, b.metric)
  33. if allowed {
  34. next.ServeHTTP(w, r)
  35. } else {
  36. limit, curr := getMetricUsage(limit, currentUsage, b.metric)
  37. apierrors.HandleAPIError(
  38. b.config,
  39. w, r,
  40. apierrors.NewErrPassThroughToClient(
  41. fmt.Errorf(UsageErrFmt, b.metric, limit, curr),
  42. http.StatusBadRequest,
  43. ),
  44. true,
  45. )
  46. }
  47. })
  48. }
  49. // checkUsage returns true if the increase in usage is allowed for the given metric,
  50. // false otherwise. We only assume increments of 1 in usage for now.
  51. func allowUsage(
  52. plan, current *types.ProjectUsage,
  53. metric types.UsageMetric,
  54. ) bool {
  55. switch metric {
  56. case types.Users:
  57. return plan.Users > current.Users+1
  58. case types.Clusters:
  59. return plan.Clusters > current.Clusters+1
  60. default:
  61. return false
  62. }
  63. }
  64. func getMetricUsage(
  65. plan, current *types.ProjectUsage,
  66. metric types.UsageMetric,
  67. ) (limit uint, curr uint) {
  68. switch metric {
  69. case types.CPU:
  70. return plan.ResourceCPU, current.ResourceCPU
  71. case types.Memory:
  72. return plan.ResourceMemory, current.ResourceMemory
  73. case types.Users:
  74. return plan.Users, current.Users
  75. case types.Clusters:
  76. return plan.Users, current.Users
  77. default:
  78. return 0, 0
  79. }
  80. }