// Copyright Project Harbor Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package exporter import ( "context" "github.com/prometheus/client_golang/prometheus" "github.com/goharbor/harbor/src/controller/blob" "github.com/goharbor/harbor/src/controller/project" "github.com/goharbor/harbor/src/controller/repository" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/lib/q" "github.com/goharbor/harbor/src/pkg/systemartifact" ) const StatisticsCollectorName = "StatisticsCollector" var ( totalUsage = typedDesc{ desc: newDescWithLables("", "statistics_total_storage_consumption", "Total storage used"), valueType: prometheus.GaugeValue, } totalProjectAmount = typedDesc{ desc: newDescWithLables("", "statistics_total_project_amount", "Total amount of projects"), valueType: prometheus.GaugeValue, } publicProjectAmount = typedDesc{ desc: newDescWithLables("", "statistics_public_project_amount", "Amount of public projects"), valueType: prometheus.GaugeValue, } privateProjectAmount = typedDesc{ desc: newDescWithLables("", "statistics_private_project_amount", "Amount of private projects"), valueType: prometheus.GaugeValue, } totalRepoAmount = typedDesc{ desc: newDescWithLables("", "statistics_total_repo_amount", "Total amount of repositories"), valueType: prometheus.GaugeValue, } publicRepoAmount = typedDesc{ desc: newDescWithLables("", "statistics_public_repo_amount", "Amount of public repositories"), valueType: prometheus.GaugeValue, } privateRepoAmount = typedDesc{ desc: newDescWithLables("", "statistics_private_repo_amount", "Amount of private repositories"), valueType: prometheus.GaugeValue, } ) type StatisticsCollector struct { proCtl project.Controller repoCtl repository.Controller blobCtl blob.Controller systemArtifactMgr systemartifact.Manager } func NewStatisticsCollector() *StatisticsCollector { return &StatisticsCollector{ blobCtl: blob.Ctl, systemArtifactMgr: systemartifact.Mgr, proCtl: project.Ctl, repoCtl: repository.Ctl, } } func (g StatisticsCollector) GetName() string { return StatisticsCollectorName } func (g StatisticsCollector) Describe(c chan<- *prometheus.Desc) { c <- totalUsage.Desc() } func (g StatisticsCollector) getTotalUsageMetric(ctx context.Context) prometheus.Metric { sum, _ := g.blobCtl.CalculateTotalSize(ctx, true) sysArtifactStorageSize, _ := g.systemArtifactMgr.GetStorageSize(ctx) return totalUsage.MustNewConstMetric(float64(sum + sysArtifactStorageSize)) } func (g StatisticsCollector) getTotalRepoAmount(ctx context.Context) int64 { n, err := g.repoCtl.Count(ctx, nil) if err != nil { log.Errorf("get total repositories error: %v", err) return 0 } return n } func (g StatisticsCollector) getTotalProjectsAmount(ctx context.Context) int64 { count, err := g.proCtl.Count(ctx, nil) if err != nil { log.Errorf("get total projects error: %v", err) return 0 } return count } func (g StatisticsCollector) getPublicProjectsAndRepositories(ctx context.Context) (int64, int64) { pubProjects, err := g.proCtl.List(ctx, q.New(q.KeyWords{"public": true}), project.Metadata(false)) if err != nil { log.Errorf("get public projects error: %v", err) } pubProjectsAmount := int64(len(pubProjects)) if pubProjectsAmount == 0 { return pubProjectsAmount, 0 } var ids []interface{} for _, p := range pubProjects { ids = append(ids, p.ProjectID) } n, err := g.repoCtl.Count(ctx, &q.Query{ Keywords: map[string]interface{}{ "ProjectID": q.NewOrList(ids), }, }) if err != nil { log.Errorf("get public repo error: %v", err) return pubProjectsAmount, 0 } return pubProjectsAmount, n } // Collect implements prometheus.Collector func (g StatisticsCollector) Collect(c chan<- prometheus.Metric) { for _, m := range g.getStatistics() { c <- m } } func (g StatisticsCollector) getStatistics() []prometheus.Metric { if CacheEnabled() { value, ok := CacheGet(StatisticsCollectorName) if ok { return value.([]prometheus.Metric) } } var ( result []prometheus.Metric ctx = orm.Context() ) var ( publicProjects, publicRepos = g.getPublicProjectsAndRepositories(ctx) totalProjects = g.getTotalProjectsAmount(ctx) totalRepos = g.getTotalRepoAmount(ctx) ) result = []prometheus.Metric{ totalRepoAmount.MustNewConstMetric(float64(totalRepos)), publicRepoAmount.MustNewConstMetric(float64(publicRepos)), privateRepoAmount.MustNewConstMetric(float64(totalRepos) - float64(publicRepos)), totalProjectAmount.MustNewConstMetric(float64(totalProjects)), publicProjectAmount.MustNewConstMetric(float64(publicProjects)), privateProjectAmount.MustNewConstMetric(float64(totalProjects) - float64(publicProjects)), g.getTotalUsageMetric(ctx), } if CacheEnabled() { CachePut(StatisticsCollectorName, result) } return result }