mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-27 19:17:47 +01:00
add storage consumption support (#14772)
Return the total storage consumption in the statistic API Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
f3260fdad1
commit
1dd3b9fd82
@ -8152,3 +8152,8 @@ definitions:
|
|||||||
format: int64
|
format: int64
|
||||||
description: The count of the total repositories, only be seen by the system admin
|
description: The count of the total repositories, only be seen by the system admin
|
||||||
x-omitempty: false
|
x-omitempty: false
|
||||||
|
total_storage_consumption:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: The total storage consumption of blobs, only be seen by the system admin
|
||||||
|
x-omitempty: false
|
@ -49,6 +49,9 @@ type Controller interface {
|
|||||||
// CalculateTotalSizeByProject returns the sum of the blob size for the project
|
// CalculateTotalSizeByProject returns the sum of the blob size for the project
|
||||||
CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeign bool) (int64, error)
|
CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeign bool) (int64, error)
|
||||||
|
|
||||||
|
// CalculateTotalSize returns the sum of all the blobs size
|
||||||
|
CalculateTotalSize(ctx context.Context, excludeForeign bool) (int64, error)
|
||||||
|
|
||||||
// Ensure create blob when it not exist.
|
// Ensure create blob when it not exist.
|
||||||
Ensure(ctx context.Context, digest string, contentType string, size int64) (int64, error)
|
Ensure(ctx context.Context, digest string, contentType string, size int64) (int64, error)
|
||||||
|
|
||||||
@ -144,6 +147,10 @@ func (c *controller) CalculateTotalSizeByProject(ctx context.Context, projectID
|
|||||||
return c.blobMgr.CalculateTotalSizeByProject(ctx, projectID, excludeForeign)
|
return c.blobMgr.CalculateTotalSizeByProject(ctx, projectID, excludeForeign)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *controller) CalculateTotalSize(ctx context.Context, excludeForeign bool) (int64, error) {
|
||||||
|
return c.blobMgr.CalculateTotalSize(ctx, excludeForeign)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *controller) Ensure(ctx context.Context, digest string, contentType string, size int64) (blobID int64, err error) {
|
func (c *controller) Ensure(ctx context.Context, digest string, contentType string, size int64) (blobID int64, err error) {
|
||||||
blob, err := c.blobMgr.Get(ctx, digest)
|
blob, err := c.blobMgr.Get(ctx, digest)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -112,6 +112,16 @@ func (suite *ControllerTestSuite) TestCalculateTotalSizeByProject() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ControllerTestSuite) TestCalculateTotalSize() {
|
||||||
|
ctx := suite.Context()
|
||||||
|
size1, err := Ctl.CalculateTotalSize(ctx, true)
|
||||||
|
Ctl.Ensure(ctx, suite.DigestString(), schema2.MediaTypeForeignLayer, 100)
|
||||||
|
Ctl.Ensure(ctx, suite.DigestString(), schema2.MediaTypeLayer, 100)
|
||||||
|
size2, err := Ctl.CalculateTotalSize(ctx, false)
|
||||||
|
suite.Nil(err)
|
||||||
|
suite.Equal(int64(200), size2-size1)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *ControllerTestSuite) TestEnsure() {
|
func (suite *ControllerTestSuite) TestEnsure() {
|
||||||
ctx := suite.Context()
|
ctx := suite.Context()
|
||||||
|
|
||||||
|
@ -62,6 +62,9 @@ type DAO interface {
|
|||||||
// SumBlobsSizeByProject returns sum size of blobs by project, skip foreign blobs when `excludeForeignLayer` is true
|
// SumBlobsSizeByProject returns sum size of blobs by project, skip foreign blobs when `excludeForeignLayer` is true
|
||||||
SumBlobsSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error)
|
SumBlobsSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error)
|
||||||
|
|
||||||
|
// SumBlobsSize returns sum size of all blobs skip foreign blobs when `excludeForeignLayer` is true
|
||||||
|
SumBlobsSize(ctx context.Context, excludeForeignLayer bool) (int64, error)
|
||||||
|
|
||||||
// CreateProjectBlob create ProjectBlob and ignore conflict on project id and blob id
|
// CreateProjectBlob create ProjectBlob and ignore conflict on project id and blob id
|
||||||
CreateProjectBlob(ctx context.Context, projectID, blobID int64) (int64, error)
|
CreateProjectBlob(ctx context.Context, projectID, blobID int64) (int64, error)
|
||||||
|
|
||||||
@ -291,6 +294,31 @@ func (d *dao) SumBlobsSizeByProject(ctx context.Context, projectID int64, exclud
|
|||||||
return totalSize, nil
|
return totalSize, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SumBlobsSize returns sum size of all blobs skip foreign blobs when `excludeForeignLayer` is true
|
||||||
|
func (d *dao) SumBlobsSize(ctx context.Context, excludeForeignLayer bool) (int64, error) {
|
||||||
|
o, err := orm.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := []interface{}{}
|
||||||
|
sql := `SELECT SUM(size) FROM blob`
|
||||||
|
if excludeForeignLayer {
|
||||||
|
foreignLayerTypes := []interface{}{
|
||||||
|
schema2.MediaTypeForeignLayer,
|
||||||
|
}
|
||||||
|
sql = fmt.Sprintf(`%s Where content_type NOT IN (%s)`, sql, orm.ParamPlaceholderForIn(len(foreignLayerTypes)))
|
||||||
|
params = append(params, foreignLayerTypes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalSize int64
|
||||||
|
if err := o.Raw(sql, params...).QueryRow(&totalSize); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *dao) CreateProjectBlob(ctx context.Context, projectID, blobID int64) (int64, error) {
|
func (d *dao) CreateProjectBlob(ctx context.Context, projectID, blobID int64) (int64, error) {
|
||||||
o, err := orm.FromContext(ctx)
|
o, err := orm.FromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -245,6 +245,21 @@ func (suite *DaoTestSuite) TestListBlobsAssociatedWithArtifact() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *DaoTestSuite) TestSumBlobsSize() {
|
||||||
|
ctx := suite.Context()
|
||||||
|
|
||||||
|
size1, err := suite.dao.SumBlobsSize(ctx, true)
|
||||||
|
suite.Nil(err)
|
||||||
|
|
||||||
|
digest1 := suite.DigestString()
|
||||||
|
suite.dao.CreateBlob(ctx, &models.Blob{Digest: digest1, Size: 999})
|
||||||
|
|
||||||
|
size2, err := suite.dao.SumBlobsSize(ctx, true)
|
||||||
|
suite.Nil(err)
|
||||||
|
|
||||||
|
suite.Equal(int64(999), size2-size1)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *DaoTestSuite) TestFindBlobsShouldUnassociatedWithProject() {
|
func (suite *DaoTestSuite) TestFindBlobsShouldUnassociatedWithProject() {
|
||||||
ctx := suite.Context()
|
ctx := suite.Context()
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ type Manager interface {
|
|||||||
// CalculateTotalSizeByProject returns total blob size by project, skip foreign blobs when `excludeForeignLayer` is true
|
// CalculateTotalSizeByProject returns total blob size by project, skip foreign blobs when `excludeForeignLayer` is true
|
||||||
CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error)
|
CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error)
|
||||||
|
|
||||||
|
// SumBlobsSize returns sum size of all blobs skip foreign blobs when `excludeForeignLayer` is true
|
||||||
|
CalculateTotalSize(ctx context.Context, excludeForeignLayer bool) (int64, error)
|
||||||
|
|
||||||
// Create create blob
|
// Create create blob
|
||||||
Create(ctx context.Context, digest string, contentType string, size int64) (int64, error)
|
Create(ctx context.Context, digest string, contentType string, size int64) (int64, error)
|
||||||
|
|
||||||
@ -139,6 +142,10 @@ func (m *manager) UselessBlobs(ctx context.Context, timeWindowHours int64) ([]*m
|
|||||||
return m.dao.GetBlobsNotRefedByProjectBlob(ctx, timeWindowHours)
|
return m.dao.GetBlobsNotRefedByProjectBlob(ctx, timeWindowHours)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *manager) CalculateTotalSize(ctx context.Context, excludeForeignLayer bool) (int64, error) {
|
||||||
|
return m.dao.SumBlobsSize(ctx, excludeForeignLayer)
|
||||||
|
}
|
||||||
|
|
||||||
// NewManager returns blob manager
|
// NewManager returns blob manager
|
||||||
func NewManager() Manager {
|
func NewManager() Manager {
|
||||||
return &manager{dao: dao.New()}
|
return &manager{dao: dao.New()}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/manifest/schema2"
|
||||||
"github.com/goharbor/harbor/src/pkg/blob/models"
|
"github.com/goharbor/harbor/src/pkg/blob/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,6 +81,21 @@ func (suite *ManagerTestSuite) TestAssociateWithProject() {
|
|||||||
suite.True(associated)
|
suite.True(associated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ManagerTestSuite) TestCalculateTotalSize() {
|
||||||
|
ctx := suite.Context()
|
||||||
|
|
||||||
|
size1, err := Mgr.CalculateTotalSize(ctx, true)
|
||||||
|
suite.Nil(err)
|
||||||
|
|
||||||
|
digest := suite.DigestString()
|
||||||
|
Mgr.Create(ctx, digest, schema2.MediaTypeLayer, 100)
|
||||||
|
|
||||||
|
size2, err := Mgr.CalculateTotalSize(ctx, true)
|
||||||
|
suite.Nil(err)
|
||||||
|
|
||||||
|
suite.Equal(int64(100), size2-size1)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *ManagerTestSuite) TestCleanupAssociationsForArtifact() {
|
func (suite *ManagerTestSuite) TestCleanupAssociationsForArtifact() {
|
||||||
ctx := suite.Context()
|
ctx := suite.Context()
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/goharbor/harbor/src/controller/blob"
|
||||||
|
|
||||||
"github.com/go-openapi/runtime/middleware"
|
"github.com/go-openapi/runtime/middleware"
|
||||||
"github.com/goharbor/harbor/src/common/security/local"
|
"github.com/goharbor/harbor/src/common/security/local"
|
||||||
@ -30,6 +31,7 @@ func newStatisticAPI() *statisticAPI {
|
|||||||
return &statisticAPI{
|
return &statisticAPI{
|
||||||
proCtl: project.Ctl,
|
proCtl: project.Ctl,
|
||||||
repoCtl: repository.Ctl,
|
repoCtl: repository.Ctl,
|
||||||
|
blobCtl: blob.Ctl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +39,7 @@ type statisticAPI struct {
|
|||||||
BaseAPI
|
BaseAPI
|
||||||
proCtl project.Controller
|
proCtl project.Controller
|
||||||
repoCtl repository.Controller
|
repoCtl repository.Controller
|
||||||
|
blobCtl blob.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statisticAPI) GetStatistic(ctx context.Context, params operation.GetStatisticParams) middleware.Responder {
|
func (s *statisticAPI) GetStatistic(ctx context.Context, params operation.GetStatisticParams) middleware.Responder {
|
||||||
@ -88,6 +91,13 @@ func (s *statisticAPI) GetStatistic(ctx context.Context, params operation.GetSta
|
|||||||
}
|
}
|
||||||
statistic.TotalRepoCount = n
|
statistic.TotalRepoCount = n
|
||||||
statistic.PrivateRepoCount = n - statistic.PublicRepoCount
|
statistic.PrivateRepoCount = n - statistic.PublicRepoCount
|
||||||
|
|
||||||
|
sum, err := s.blobCtl.CalculateTotalSize(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return s.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
statistic.TotalStorageConsumption = sum
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var privProjectIDs []interface{}
|
var privProjectIDs []interface{}
|
||||||
if sc, ok := securityCtx.(*local.SecurityContext); ok && sc.IsAuthenticated() {
|
if sc, ok := securityCtx.(*local.SecurityContext); ok && sc.IsAuthenticated() {
|
||||||
|
@ -63,6 +63,27 @@ func (_m *Controller) AssociateWithProjectByID(ctx context.Context, blobID int64
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalculateTotalSize provides a mock function with given fields: ctx, excludeForeign
|
||||||
|
func (_m *Controller) CalculateTotalSize(ctx context.Context, excludeForeign bool) (int64, error) {
|
||||||
|
ret := _m.Called(ctx, excludeForeign)
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, bool) int64); ok {
|
||||||
|
r0 = rf(ctx, excludeForeign)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok {
|
||||||
|
r1 = rf(ctx, excludeForeign)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// CalculateTotalSizeByProject provides a mock function with given fields: ctx, projectID, excludeForeign
|
// CalculateTotalSizeByProject provides a mock function with given fields: ctx, projectID, excludeForeign
|
||||||
func (_m *Controller) CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeign bool) (int64, error) {
|
func (_m *Controller) CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeign bool) (int64, error) {
|
||||||
ret := _m.Called(ctx, projectID, excludeForeign)
|
ret := _m.Called(ctx, projectID, excludeForeign)
|
||||||
|
@ -58,6 +58,27 @@ func (_m *Manager) AssociateWithProject(ctx context.Context, blobID int64, proje
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalculateTotalSize provides a mock function with given fields: ctx, excludeForeignLayer
|
||||||
|
func (_m *Manager) CalculateTotalSize(ctx context.Context, excludeForeignLayer bool) (int64, error) {
|
||||||
|
ret := _m.Called(ctx, excludeForeignLayer)
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, bool) int64); ok {
|
||||||
|
r0 = rf(ctx, excludeForeignLayer)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok {
|
||||||
|
r1 = rf(ctx, excludeForeignLayer)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// CalculateTotalSizeByProject provides a mock function with given fields: ctx, projectID, excludeForeignLayer
|
// CalculateTotalSizeByProject provides a mock function with given fields: ctx, projectID, excludeForeignLayer
|
||||||
func (_m *Manager) CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error) {
|
func (_m *Manager) CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error) {
|
||||||
ret := _m.Called(ctx, projectID, excludeForeignLayer)
|
ret := _m.Called(ctx, projectID, excludeForeignLayer)
|
||||||
|
Loading…
Reference in New Issue
Block a user