mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 02:35:17 +01:00
Merge pull request #8750 from wy65701436/gc-fix-quota
fix quota size usage in gc job
This commit is contained in:
commit
af845bdab5
@ -108,7 +108,21 @@ func GetBlobsNotInProject(projectID int64, blobDigests ...string) ([]*models.Blo
|
|||||||
func CountSizeOfProject(pid int64) (int64, error) {
|
func CountSizeOfProject(pid int64) (int64, error) {
|
||||||
var blobs []models.Blob
|
var blobs []models.Blob
|
||||||
|
|
||||||
_, err := GetOrmer().Raw(`SELECT bb.id, bb.digest, bb.content_type, bb.size, bb.creation_time FROM project_blob pb LEFT JOIN blob bb ON pb.blob_id = bb.id WHERE pb.project_id = ? `, pid).QueryRows(&blobs)
|
sql := `
|
||||||
|
SELECT
|
||||||
|
DISTINCT bb.digest,
|
||||||
|
bb.id,
|
||||||
|
bb.content_type,
|
||||||
|
bb.size,
|
||||||
|
bb.creation_time
|
||||||
|
FROM artifact af
|
||||||
|
JOIN artifact_blob afnb
|
||||||
|
ON af.digest = afnb.digest_af
|
||||||
|
JOIN BLOB bb
|
||||||
|
ON afnb.digest_blob = bb.digest
|
||||||
|
WHERE af.project_id = ?
|
||||||
|
`
|
||||||
|
_, err := GetOrmer().Raw(sql, pid).QueryRows(&blobs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -40,29 +40,159 @@ func TestHasBlobInProject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCountSizeOfProject(t *testing.T) {
|
func TestCountSizeOfProject(t *testing.T) {
|
||||||
id1, err := AddBlob(&models.Blob{
|
_, err := AddBlob(&models.Blob{
|
||||||
Digest: "CountSizeOfProject_blob1",
|
Digest: "CountSizeOfProject_blob1",
|
||||||
Size: 101,
|
Size: 101,
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
id2, err := AddBlob(&models.Blob{
|
_, err = AddBlob(&models.Blob{
|
||||||
Digest: "CountSizeOfProject_blob2",
|
Digest: "CountSizeOfProject_blob2",
|
||||||
Size: 202,
|
Size: 202,
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = AddBlob(&models.Blob{
|
||||||
|
Digest: "CountSizeOfProject_blob3",
|
||||||
|
Size: 303,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
pid1, err := AddProject(models.Project{
|
pid1, err := AddProject(models.Project{
|
||||||
Name: "CountSizeOfProject_project1",
|
Name: "CountSizeOfProject_project1",
|
||||||
OwnerID: 1,
|
OwnerID: 1,
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
_, err = AddBlobToProject(id1, pid1)
|
af := &models.Artifact{
|
||||||
|
PID: pid1,
|
||||||
|
Repo: "hello-world",
|
||||||
|
Tag: "v1",
|
||||||
|
Digest: "CountSizeOfProject_af1",
|
||||||
|
Kind: "image",
|
||||||
|
}
|
||||||
|
|
||||||
|
// add
|
||||||
|
_, err = AddArtifact(af)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
_, err = AddBlobToProject(id2, pid1)
|
|
||||||
|
afnb1 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af1",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob1",
|
||||||
|
}
|
||||||
|
afnb2 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af1",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob2",
|
||||||
|
}
|
||||||
|
afnb3 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af1",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob3",
|
||||||
|
}
|
||||||
|
|
||||||
|
var afnbs []*models.ArtifactAndBlob
|
||||||
|
afnbs = append(afnbs, afnb1)
|
||||||
|
afnbs = append(afnbs, afnb2)
|
||||||
|
afnbs = append(afnbs, afnb3)
|
||||||
|
|
||||||
|
// add
|
||||||
|
err = AddArtifactNBlobs(afnbs)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
pSize, err := CountSizeOfProject(pid1)
|
pSize, err := CountSizeOfProject(pid1)
|
||||||
assert.Equal(t, pSize, int64(303))
|
assert.Equal(t, pSize, int64(606))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCountSizeOfProjectDupdigest(t *testing.T) {
|
||||||
|
_, err := AddBlob(&models.Blob{
|
||||||
|
Digest: "CountSizeOfProject_blob11",
|
||||||
|
Size: 101,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = AddBlob(&models.Blob{
|
||||||
|
Digest: "CountSizeOfProject_blob22",
|
||||||
|
Size: 202,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = AddBlob(&models.Blob{
|
||||||
|
Digest: "CountSizeOfProject_blob33",
|
||||||
|
Size: 303,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = AddBlob(&models.Blob{
|
||||||
|
Digest: "CountSizeOfProject_blob44",
|
||||||
|
Size: 404,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
pid1, err := AddProject(models.Project{
|
||||||
|
Name: "CountSizeOfProject_project11",
|
||||||
|
OwnerID: 1,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// add af1 into project
|
||||||
|
af1 := &models.Artifact{
|
||||||
|
PID: pid1,
|
||||||
|
Repo: "hello-world",
|
||||||
|
Tag: "v1",
|
||||||
|
Digest: "CountSizeOfProject_af11",
|
||||||
|
Kind: "image",
|
||||||
|
}
|
||||||
|
_, err = AddArtifact(af1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
afnb11 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af11",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob11",
|
||||||
|
}
|
||||||
|
afnb12 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af11",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob22",
|
||||||
|
}
|
||||||
|
afnb13 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af11",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob33",
|
||||||
|
}
|
||||||
|
var afnbs1 []*models.ArtifactAndBlob
|
||||||
|
afnbs1 = append(afnbs1, afnb11)
|
||||||
|
afnbs1 = append(afnbs1, afnb12)
|
||||||
|
afnbs1 = append(afnbs1, afnb13)
|
||||||
|
err = AddArtifactNBlobs(afnbs1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// add af2 into project
|
||||||
|
af2 := &models.Artifact{
|
||||||
|
PID: pid1,
|
||||||
|
Repo: "hello-world",
|
||||||
|
Tag: "v2",
|
||||||
|
Digest: "CountSizeOfProject_af22",
|
||||||
|
Kind: "image",
|
||||||
|
}
|
||||||
|
_, err = AddArtifact(af2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
afnb21 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af22",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob11",
|
||||||
|
}
|
||||||
|
afnb22 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af22",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob22",
|
||||||
|
}
|
||||||
|
afnb23 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af22",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob33",
|
||||||
|
}
|
||||||
|
afnb24 := &models.ArtifactAndBlob{
|
||||||
|
DigestAF: "CountSizeOfProject_af22",
|
||||||
|
DigestBlob: "CountSizeOfProject_blob44",
|
||||||
|
}
|
||||||
|
var afnbs2 []*models.ArtifactAndBlob
|
||||||
|
afnbs2 = append(afnbs2, afnb21)
|
||||||
|
afnbs2 = append(afnbs2, afnb22)
|
||||||
|
afnbs2 = append(afnbs2, afnb23)
|
||||||
|
afnbs2 = append(afnbs2, afnb24)
|
||||||
|
err = AddArtifactNBlobs(afnbs2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
pSize, err := CountSizeOfProject(pid1)
|
||||||
|
assert.Equal(t, pSize, int64(1010))
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,16 @@ func (m *Manager) UpdateQuota(hardLimits types.ResourceList) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetResourceUsage sets the usage per resource name
|
||||||
|
func (m *Manager) SetResourceUsage(resource types.ResourceName, value int64) error {
|
||||||
|
o := dao.GetOrmer()
|
||||||
|
|
||||||
|
sql := fmt.Sprintf("UPDATE quota_usage SET used = jsonb_set(used, '{%s}', to_jsonb(%d::int), true) WHERE reference = ? AND reference_id = ?", resource, value)
|
||||||
|
_, err := o.Raw(sql, m.reference, m.referenceID).Exec()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// EnsureQuota ensures the reference has quota and usage,
|
// EnsureQuota ensures the reference has quota and usage,
|
||||||
// if non-existent, will create new quota and usage.
|
// if non-existent, will create new quota and usage.
|
||||||
// if existent, update the quota and usage.
|
// if existent, update the quota and usage.
|
||||||
|
@ -132,6 +132,24 @@ func (suite *ManagerSuite) TestUpdateQuota() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ManagerSuite) TestSetResourceUsage() {
|
||||||
|
mgr := suite.quotaManager()
|
||||||
|
id, _ := mgr.NewQuota(hardLimits)
|
||||||
|
|
||||||
|
if err := mgr.SetResourceUsage(types.ResourceCount, 123); suite.Nil(err) {
|
||||||
|
quota, _ := dao.GetQuota(id)
|
||||||
|
suite.Equal(hardLimits, mustResourceList(quota.Hard))
|
||||||
|
|
||||||
|
usage, _ := dao.GetQuotaUsage(id)
|
||||||
|
suite.Equal(types.ResourceList{types.ResourceCount: 123, types.ResourceStorage: 0}, mustResourceList(usage.Used))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.SetResourceUsage(types.ResourceStorage, 234); suite.Nil(err) {
|
||||||
|
usage, _ := dao.GetQuotaUsage(id)
|
||||||
|
suite.Equal(types.ResourceList{types.ResourceCount: 123, types.ResourceStorage: 234}, mustResourceList(usage.Used))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *ManagerSuite) TestEnsureQuota() {
|
func (suite *ManagerSuite) TestEnsureQuota() {
|
||||||
// non-existent
|
// non-existent
|
||||||
nonExistRefID := "3"
|
nonExistRefID := "3"
|
||||||
|
@ -22,10 +22,14 @@ import (
|
|||||||
"github.com/garyburd/redigo/redis"
|
"github.com/garyburd/redigo/redis"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/config"
|
"github.com/goharbor/harbor/src/common/config"
|
||||||
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
|
common_quota "github.com/goharbor/harbor/src/common/quota"
|
||||||
"github.com/goharbor/harbor/src/common/registryctl"
|
"github.com/goharbor/harbor/src/common/registryctl"
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/types"
|
||||||
"github.com/goharbor/harbor/src/registryctl/client"
|
"github.com/goharbor/harbor/src/registryctl/client"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -88,6 +92,9 @@ func (gc *GarbageCollector) Run(ctx job.Context, params job.Parameters) error {
|
|||||||
if err := gc.cleanCache(); err != nil {
|
if err := gc.cleanCache(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := gc.ensureQuota(); err != nil {
|
||||||
|
gc.logger.Warningf("failed to align quota data in gc job, with error: %v", err)
|
||||||
|
}
|
||||||
gc.logger.Infof("GC results: status: %t, message: %s, start: %s, end: %s.", gcr.Status, gcr.Msg, gcr.StartTime, gcr.EndTime)
|
gc.logger.Infof("GC results: status: %t, message: %s, start: %s, end: %s.", gcr.Status, gcr.Msg, gcr.StartTime, gcr.EndTime)
|
||||||
gc.logger.Infof("success to run gc in job.")
|
gc.logger.Infof("success to run gc in job.")
|
||||||
return nil
|
return nil
|
||||||
@ -193,3 +200,27 @@ func delKeys(con redis.Conn, pattern string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gc *GarbageCollector) ensureQuota() error {
|
||||||
|
projects, err := dao.GetProjects(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, project := range projects {
|
||||||
|
pSize, err := dao.CountSizeOfProject(project.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
gc.logger.Warningf("error happen on counting size of project:%d by artifact, error:%v, just skip it.", project.ProjectID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
quotaMgr, err := common_quota.NewManager("project", strconv.FormatInt(project.ProjectID, 10))
|
||||||
|
if err != nil {
|
||||||
|
gc.logger.Errorf("Error occurred when to new quota manager %v, just skip it.", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := quotaMgr.SetResourceUsage(types.ResourceStorage, pSize); err != nil {
|
||||||
|
gc.logger.Errorf("cannot ensure quota for the project: %d, err: %v, just skip it.", project.ProjectID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user