| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- // +build ee
- package usage
- import (
- "fmt"
- "sync"
- "time"
- "github.com/porter-dev/porter/api/server/shared/config/env"
- "github.com/porter-dev/porter/api/types"
- "github.com/porter-dev/porter/ee/integrations/vault"
- "github.com/porter-dev/porter/internal/adapter"
- "github.com/porter-dev/porter/internal/models"
- "github.com/porter-dev/porter/internal/oauth"
- "github.com/porter-dev/porter/internal/repository"
- "github.com/porter-dev/porter/internal/usage"
- "golang.org/x/oauth2"
- "gorm.io/gorm"
- "github.com/porter-dev/porter/internal/repository/credentials"
- rgorm "github.com/porter-dev/porter/internal/repository/gorm"
- )
- type UsageTracker struct {
- db *gorm.DB
- repo repository.Repository
- doConf *oauth2.Config
- whitelistedUsers map[uint]uint
- }
- type UsageTrackerOpts struct {
- DBConf *env.DBConf
- DOClientID string
- DOClientSecret string
- DOScopes []string
- ServerURL string
- WhitelistedUsers map[uint]uint
- }
- const stepSize = 100
- func NewUsageTracker(opts *UsageTrackerOpts) (*UsageTracker, error) {
- db, err := adapter.New(opts.DBConf)
- if err != nil {
- return nil, err
- }
- var credBackend credentials.CredentialStorage
- if opts.DBConf.VaultAPIKey != "" && opts.DBConf.VaultServerURL != "" && opts.DBConf.VaultPrefix != "" {
- credBackend = vault.NewClient(
- opts.DBConf.VaultServerURL,
- opts.DBConf.VaultAPIKey,
- opts.DBConf.VaultPrefix,
- )
- }
- var key [32]byte
- for i, b := range []byte(opts.DBConf.EncryptionKey) {
- key[i] = b
- }
- repo := rgorm.NewRepository(db, &key, credBackend)
- doConf := oauth.NewDigitalOceanClient(&oauth.Config{
- ClientID: opts.DOClientID,
- ClientSecret: opts.DOClientSecret,
- Scopes: opts.DOScopes,
- BaseURL: opts.ServerURL,
- })
- return &UsageTracker{db, repo, doConf, opts.WhitelistedUsers}, nil
- }
- type UsageTrackerResponse struct {
- CPULimit uint
- CPUUsage uint
- MemoryLimit uint
- MemoryUsage uint
- UserLimit uint
- UserUsage uint
- ClusterLimit uint
- ClusterUsage uint
- Exceeded bool
- ExceededSince time.Time
- Project models.Project
- AdminEmails []string
- }
- func (u *UsageTracker) GetProjectUsage() (map[uint]*UsageTrackerResponse, error) {
- res := make(map[uint]*UsageTrackerResponse)
- // get the count of the projects
- var count int64
- if err := u.db.Model(&models.Project{}).Count(&count).Error; err != nil {
- return nil, err
- }
- var mu sync.Mutex
- var wg sync.WaitGroup
- worker := func(project *models.Project) {
- defer wg.Done()
- current, limit, cache, err := usage.GetUsage(&usage.GetUsageOpts{
- Repo: u.repo,
- DOConf: u.doConf,
- Project: project,
- WhitelistedUsers: u.whitelistedUsers,
- })
- if err != nil {
- fmt.Printf("Project %d: error getting usage: %v\n", project.ID, err)
- return
- }
- // get the admin emails for the project
- roles, err := u.repo.Project().ListProjectRoles(project.ID)
- if err != nil {
- fmt.Printf("Project %d: error getting admin emails: %v\n", project.ID, err)
- return
- }
- adminEmails := make([]string, 0)
- for _, role := range roles {
- if role.Kind == types.RoleAdmin {
- user, err := u.repo.User().ReadUser(role.UserID)
- if err != nil {
- continue
- }
- adminEmails = append(adminEmails, user.Email)
- }
- }
- exceededSince := cache.ExceededSince
- if exceededSince == nil {
- now := time.Now()
- exceededSince = &now
- }
- mu.Lock()
- res[project.ID] = &UsageTrackerResponse{
- CPUUsage: cache.ResourceCPU,
- CPULimit: limit.ResourceCPU,
- MemoryUsage: cache.ResourceMemory,
- MemoryLimit: limit.ResourceMemory,
- UserUsage: current.Users,
- UserLimit: limit.Users,
- ClusterUsage: current.Clusters,
- ClusterLimit: limit.Clusters,
- Exceeded: cache.Exceeded,
- ExceededSince: *exceededSince,
- Project: *project,
- AdminEmails: adminEmails,
- }
- mu.Unlock()
- return
- }
- // iterate (count / stepSize) + 1 times using Limit and Offset
- for i := 0; i < (int(count)/stepSize)+1; i++ {
- projects := []*models.Project{}
- if err := u.db.Order("id asc").Offset(i * stepSize).Limit(stepSize).Find(&projects).Error; err != nil {
- return nil, err
- }
- // go through each project
- for _, project := range projects {
- wg.Add(1)
- go worker(project)
- }
- wg.Wait()
- }
- return res, nil
- }
|