Project name validation to prevent system artifact reserved project names (#17029)

Include system artifact size in system storage space calculation.

Signed-off-by: prahaladdarkin <prahaladd@vmware.com>
This commit is contained in:
prahaladdarkin 2022-06-21 10:21:29 +05:30 committed by GitHub
parent aab320591f
commit 7024442f25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 7 deletions

View File

@ -19,6 +19,7 @@ var (
)
const repositoryFormat = "sys_harbor/%s/%s"
const systemArtifactProjectName = "sys_h@rb0r"
// Manager provides a low-level interface for harbor services
// to create registry artifacts containing arbitrary data but which
@ -64,6 +65,10 @@ type Manager interface {
// artifact records selected by the Selector registered for each vendor type.
// Returns the total number of records deleted, the reclaimed size and any error (if encountered)
Cleanup(ctx context.Context) (int64, int64, error)
// GetSystemArtifactProjectNames returns a list of project names that are reserved
// to be used by the system artifact manager for creation of system artifacts.
GetSystemArtifactProjectNames() []string
}
type systemArtifactManager struct {
@ -167,6 +172,10 @@ func (mgr *systemArtifactManager) GetCleanupCriteria(vendor string, artifactType
return DefaultSelector
}
func (mgr *systemArtifactManager) GetSystemArtifactProjectNames() []string {
return []string{systemArtifactProjectName}
}
func (mgr *systemArtifactManager) Cleanup(ctx context.Context) (int64, int64, error) {
logger.Info("Starting system artifact cleanup")
// clean up artifact records having customized cleanup criteria first

View File

@ -478,6 +478,12 @@ func (suite *ManagerTestSuite) TestCleanupErrorForVendor() {
suite.NoErrorf(err, "Expected no error, but was %v", err)
}
func (suite *ManagerTestSuite) TestGetSystemArtifactProjectNames() {
reservedProjectNames := suite.mgr.GetSystemArtifactProjectNames()
suite.Equalf(1, len(reservedProjectNames), "Expected: %d, Actual: %d", 1, len(reservedProjectNames))
suite.Equalf(systemArtifactProjectName, reservedProjectNames[0], "Expected: %s, Actual: %s", systemArtifactProjectName, reservedProjectNames[0])
}
func (suite *ManagerTestSuite) List(ctx context.Context) ([]*model.SystemArtifact, error) {
return make([]*model.SystemArtifact, 0), nil
}

View File

@ -17,6 +17,7 @@ package handler
import (
"context"
"github.com/goharbor/harbor/src/controller/blob"
"github.com/goharbor/harbor/src/pkg/systemartifact"
"github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/common/security/local"
@ -29,17 +30,19 @@ import (
func newStatisticAPI() *statisticAPI {
return &statisticAPI{
proCtl: project.Ctl,
repoCtl: repository.Ctl,
blobCtl: blob.Ctl,
proCtl: project.Ctl,
repoCtl: repository.Ctl,
blobCtl: blob.Ctl,
systemArtifactMgr: systemartifact.Mgr,
}
}
type statisticAPI struct {
BaseAPI
proCtl project.Controller
repoCtl repository.Controller
blobCtl blob.Controller
proCtl project.Controller
repoCtl repository.Controller
blobCtl blob.Controller
systemArtifactMgr systemartifact.Manager
}
func (s *statisticAPI) GetStatistic(ctx context.Context, params operation.GetStatisticParams) middleware.Responder {
@ -96,7 +99,13 @@ func (s *statisticAPI) GetStatistic(ctx context.Context, params operation.GetSta
if err != nil {
return s.SendError(ctx, err)
}
statistic.TotalStorageConsumption = sum
sysArtifactStorageSize, err := s.systemArtifactMgr.GetStorageSize(ctx)
if err != nil {
return s.SendError(ctx, err)
}
statistic.TotalStorageConsumption = sum + sysArtifactStorageSize
} else {
var privProjectIDs []interface{}
if sc, ok := securityCtx.(*local.SecurityContext); ok && sc.IsAuthenticated() {

View File

@ -0,0 +1,63 @@
package handler
import (
"github.com/goharbor/harbor/src/pkg/project/models"
models2 "github.com/goharbor/harbor/src/server/v2.0/models"
"github.com/goharbor/harbor/src/server/v2.0/restapi"
blobtesting "github.com/goharbor/harbor/src/testing/controller/blob"
projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
repositorytesting "github.com/goharbor/harbor/src/testing/controller/repository"
"github.com/goharbor/harbor/src/testing/mock"
systemartifacttesting "github.com/goharbor/harbor/src/testing/pkg/systemartifact"
htesting "github.com/goharbor/harbor/src/testing/server/v2.0/handler"
"github.com/stretchr/testify/suite"
"testing"
)
type StatisticsTestSuite struct {
htesting.Suite
projectCtl *projecttesting.Controller
repoCtl *repositorytesting.Controller
blobCtl *blobtesting.Controller
sysArtifactMgr *systemartifacttesting.Manager
}
func (suite *StatisticsTestSuite) SetupSuite() {
suite.projectCtl = &projecttesting.Controller{}
suite.repoCtl = &repositorytesting.Controller{}
suite.blobCtl = &blobtesting.Controller{}
suite.sysArtifactMgr = &systemartifacttesting.Manager{}
suite.Config = &restapi.Config{StatisticAPI: &statisticAPI{
proCtl: suite.projectCtl,
repoCtl: suite.repoCtl,
blobCtl: suite.blobCtl,
systemArtifactMgr: suite.sysArtifactMgr,
}}
suite.Suite.SetupSuite()
}
func (suite *StatisticsTestSuite) TestGetStatistic() {
projects := make([]*models.Project, 0)
suite.projectCtl.On("List", mock.Anything, mock.Anything, mock.Anything).Return(projects, nil)
suite.projectCtl.On("Count", mock.Anything, mock.Anything).Return(int64(10), nil)
suite.repoCtl.On("Count", mock.Anything, mock.Anything).Return(int64(20), nil)
suite.blobCtl.On("CalculateTotalSize", mock.Anything, true).Return(int64(1000), nil)
suite.sysArtifactMgr.On("GetStorageSize", mock.Anything).Return(int64(1000), nil)
suite.Security.On("IsAuthenticated").Return(true)
suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(true)
suite.Security.On("IsSysAdmin").Return(true)
var statistics models2.Statistic
res, err := suite.GetJSON("/statistics", &statistics)
suite.NoError(err)
suite.Equal(200, res.StatusCode)
suite.Equal(int64(2000), statistics.TotalStorageConsumption)
suite.Equal(int64(10), statistics.PrivateProjectCount)
suite.Equal(int64(20), statistics.PrivateRepoCount)
suite.Equal(int64(20), statistics.TotalRepoCount)
}
func TestStatisticsSuite(t *testing.T) {
suite.Run(t, &StatisticsTestSuite{})
}

View File

@ -139,6 +139,22 @@ func (_m *Manager) GetStorageSize(ctx context.Context) (int64, error) {
return r0, r1
}
// GetSystemArtifactProjectNames provides a mock function with given fields:
func (_m *Manager) GetSystemArtifactProjectNames() []string {
ret := _m.Called()
var r0 []string
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
return r0
}
// Read provides a mock function with given fields: ctx, vendor, repository, digest
func (_m *Manager) Read(ctx context.Context, vendor string, repository string, digest string) (io.ReadCloser, error) {
ret := _m.Called(ctx, vendor, repository, digest)