mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-11 10:27:58 +01:00
fix catalog performance issue (#14120)
Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
a211b0c9d7
commit
1b85c67f63
@ -15,6 +15,7 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
@ -51,6 +52,7 @@ var (
|
||||
middleware.MethodAndPathSkipper(http.MethodGet, distribution.BlobURLRegexp),
|
||||
middleware.MethodAndPathSkipper(http.MethodPatch, distribution.BlobUploadURLRegexp),
|
||||
middleware.MethodAndPathSkipper(http.MethodPut, distribution.BlobUploadURLRegexp),
|
||||
middleware.MethodAndPathSkipper(http.MethodGet, lib.V2CatalogURLRe),
|
||||
pingSkipper,
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
o "github.com/astaxie/beego/orm"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
@ -40,6 +41,8 @@ type DAO interface {
|
||||
Update(ctx context.Context, repository *models.RepoRecord, props ...string) (err error)
|
||||
// AddPullCount increase one pull count for the specified repository
|
||||
AddPullCount(ctx context.Context, id int64) error
|
||||
// NonEmptyRepos returns the repositories without any artifact or all the artifacts are untagged.
|
||||
NonEmptyRepos(ctx context.Context) ([]*models.RepoRecord, error)
|
||||
}
|
||||
|
||||
// New returns an instance of the default DAO
|
||||
@ -155,3 +158,19 @@ func (d *dao) AddPullCount(ctx context.Context, id int64) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dao) NonEmptyRepos(ctx context.Context) ([]*models.RepoRecord, error) {
|
||||
var repos []*models.RepoRecord
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf(`select r.* from repository as r LEFT JOIN tag as t on r.repository_id = t.repository_id where t.repository_id is not null;`)
|
||||
_, err = ormer.Raw(sql).QueryRows(&repos)
|
||||
if err != nil {
|
||||
return repos, err
|
||||
}
|
||||
|
||||
return repos, nil
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ import (
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
af_dao "github.com/goharbor/harbor/src/pkg/artifact/dao"
|
||||
tag_dao "github.com/goharbor/harbor/src/pkg/tag/dao"
|
||||
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
"time"
|
||||
@ -34,13 +38,17 @@ var (
|
||||
|
||||
type daoTestSuite struct {
|
||||
suite.Suite
|
||||
dao DAO
|
||||
id int64
|
||||
ctx context.Context
|
||||
dao DAO
|
||||
tagDao tag_dao.DAO
|
||||
afDao af_dao.DAO
|
||||
id int64
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (d *daoTestSuite) SetupSuite() {
|
||||
d.dao = New()
|
||||
d.tagDao = tag_dao.New()
|
||||
d.afDao = af_dao.New()
|
||||
common_dao.PrepareTestForPostgresSQL()
|
||||
d.ctx = orm.NewContext(nil, beegoorm.NewOrm())
|
||||
}
|
||||
@ -182,6 +190,57 @@ func (d *daoTestSuite) TestAddPullCount() {
|
||||
d.dao.Delete(d.ctx, id)
|
||||
}
|
||||
|
||||
func (d *daoTestSuite) TestEmptyRepos() {
|
||||
repository := &models.RepoRecord{
|
||||
Name: "TestEmptyRepos",
|
||||
ProjectID: 10,
|
||||
Description: "test pull count",
|
||||
PullCount: 1,
|
||||
}
|
||||
id, err := d.dao.Create(d.ctx, repository)
|
||||
d.Require().Nil(err)
|
||||
|
||||
art := &af_dao.Artifact{
|
||||
Type: "IMAGE",
|
||||
MediaType: v1.MediaTypeImageConfig,
|
||||
ManifestMediaType: v1.MediaTypeImageIndex,
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
RepositoryName: "library/hello-world",
|
||||
Digest: "parent_digest",
|
||||
PushTime: time.Now(),
|
||||
PullTime: time.Now(),
|
||||
Annotations: `{"anno1":"value1"}`,
|
||||
}
|
||||
afID, err := d.afDao.Create(d.ctx, art)
|
||||
d.Require().Nil(err)
|
||||
|
||||
tag := &tag.Tag{
|
||||
RepositoryID: id,
|
||||
ArtifactID: afID,
|
||||
Name: "latest",
|
||||
PushTime: time.Now(),
|
||||
PullTime: time.Now(),
|
||||
}
|
||||
_, err = d.tagDao.Create(d.ctx, tag)
|
||||
d.Require().Nil(err)
|
||||
|
||||
repos, err := d.dao.NonEmptyRepos(d.ctx)
|
||||
d.Require().Nil(err)
|
||||
|
||||
var success bool
|
||||
for _, repo := range repos {
|
||||
if repo.Name == "TestEmptyRepos" {
|
||||
success = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !success {
|
||||
d.Fail("TestEmptyRepos failure")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaoTestSuite(t *testing.T) {
|
||||
suite.Run(t, &daoTestSuite{})
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ type Manager interface {
|
||||
Update(ctx context.Context, repository *models.RepoRecord, props ...string) (err error)
|
||||
// AddPullCount increase one pull count for the specified repository
|
||||
AddPullCount(ctx context.Context, id int64) error
|
||||
// NonEmptyRepos returns the repositories without any artifact or all the artifacts are untagged.
|
||||
NonEmptyRepos(ctx context.Context) ([]*models.RepoRecord, error)
|
||||
}
|
||||
|
||||
// New returns a default implementation of Manager
|
||||
@ -102,3 +104,7 @@ func (m *manager) Update(ctx context.Context, repository *models.RepoRecord, pro
|
||||
func (m *manager) AddPullCount(ctx context.Context, id int64) error {
|
||||
return m.dao.AddPullCount(ctx, id)
|
||||
}
|
||||
|
||||
func (m *manager) NonEmptyRepos(ctx context.Context) ([]*models.RepoRecord, error) {
|
||||
return m.dao.NonEmptyRepos(ctx)
|
||||
}
|
||||
|
@ -17,63 +17,31 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/repository/dao"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type fakeDao struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *fakeDao) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) List(ctx context.Context, query *q.Query) ([]*models.RepoRecord, error) {
|
||||
args := f.Called()
|
||||
return args.Get(0).([]*models.RepoRecord), args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) Get(ctx context.Context, id int64) (*models.RepoRecord, error) {
|
||||
args := f.Called()
|
||||
return args.Get(0).(*models.RepoRecord), args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) Create(ctx context.Context, repository *models.RepoRecord) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) Delete(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
func (f *fakeDao) Update(ctx context.Context, repository *models.RepoRecord, props ...string) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
func (f *fakeDao) AddPullCount(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
type managerTestSuite struct {
|
||||
suite.Suite
|
||||
mgr *manager
|
||||
dao *fakeDao
|
||||
dao *dao.DAO
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) SetupTest() {
|
||||
m.dao = &fakeDao{}
|
||||
m.dao = &dao.DAO{}
|
||||
m.mgr = &manager{
|
||||
dao: m.dao,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestCount() {
|
||||
m.dao.On("Count", mock.Anything).Return(1, nil)
|
||||
total, err := m.mgr.Count(nil, nil)
|
||||
m.Require().Nil(err)
|
||||
m.Equal(int64(1), total)
|
||||
m.dao.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
n, err := m.mgr.Count(context.Background(), nil)
|
||||
m.Nil(err)
|
||||
m.Equal(int64(1), n)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestList() {
|
||||
@ -82,11 +50,11 @@ func (m *managerTestSuite) TestList() {
|
||||
ProjectID: 1,
|
||||
Name: "library/hello-world",
|
||||
}
|
||||
m.dao.On("List", mock.Anything).Return([]*models.RepoRecord{repository}, nil)
|
||||
repositories, err := m.mgr.List(nil, nil)
|
||||
m.Require().Nil(err)
|
||||
m.Equal(1, len(repositories))
|
||||
m.Equal(repository.RepositoryID, repositories[0].RepositoryID)
|
||||
m.dao.On("List", mock.Anything, mock.Anything).Return([]*models.RepoRecord{repository}, nil)
|
||||
rpers, err := m.mgr.List(context.Background(), nil)
|
||||
m.Nil(err)
|
||||
m.Equal(1, len(rpers))
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestGet() {
|
||||
@ -95,8 +63,8 @@ func (m *managerTestSuite) TestGet() {
|
||||
ProjectID: 1,
|
||||
Name: "library/hello-world",
|
||||
}
|
||||
m.dao.On("Get", mock.Anything).Return(repository, nil)
|
||||
repo, err := m.mgr.Get(nil, 1)
|
||||
m.dao.On("Get", mock.Anything, mock.Anything).Return(repository, nil)
|
||||
repo, err := m.mgr.Get(context.Background(), 1)
|
||||
m.Require().Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
m.Require().NotNil(repo)
|
||||
@ -109,8 +77,8 @@ func (m *managerTestSuite) TestGetByName() {
|
||||
ProjectID: 1,
|
||||
Name: "library/hello-world",
|
||||
}
|
||||
m.dao.On("List", mock.Anything).Return([]*models.RepoRecord{repository}, nil)
|
||||
repo, err := m.mgr.GetByName(nil, "library/hello-world")
|
||||
m.dao.On("List", mock.Anything, mock.Anything).Return([]*models.RepoRecord{repository}, nil)
|
||||
repo, err := m.mgr.GetByName(context.Background(), "library/hello-world")
|
||||
m.Require().Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
m.Require().NotNil(repo)
|
||||
@ -118,39 +86,46 @@ func (m *managerTestSuite) TestGetByName() {
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestCreate() {
|
||||
m.dao.On("Create", mock.Anything).Return(1, nil)
|
||||
id, err := m.mgr.Create(nil, &models.RepoRecord{
|
||||
ProjectID: 1,
|
||||
Name: "library/hello-world",
|
||||
})
|
||||
m.Require().Nil(err)
|
||||
m.dao.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
_, err := m.mgr.Create(context.Background(), &models.RepoRecord{})
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
m.Equal(int64(1), id)
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestDelete() {
|
||||
m.dao.On("Delete", mock.Anything).Return(nil)
|
||||
err := m.mgr.Delete(nil, 1)
|
||||
m.Require().Nil(err)
|
||||
m.dao.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
err := m.mgr.Delete(context.Background(), 1)
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestUpdate() {
|
||||
m.dao.On("Update", mock.Anything).Return(nil)
|
||||
err := m.mgr.Update(nil, &models.RepoRecord{
|
||||
RepositoryID: 1,
|
||||
})
|
||||
m.Require().Nil(err)
|
||||
m.dao.On("Update", mock.Anything, mock.Anything).Return(nil)
|
||||
err := m.mgr.Update(context.Background(), &models.RepoRecord{})
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestAddPullCount() {
|
||||
m.dao.On("AddPullCount", mock.Anything).Return(nil)
|
||||
err := m.mgr.AddPullCount(nil, 1)
|
||||
m.dao.On("AddPullCount", mock.Anything, mock.Anything).Return(nil)
|
||||
err := m.mgr.AddPullCount(context.Background(), 1)
|
||||
m.Require().Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestNonEmptyRepos() {
|
||||
repository := &models.RepoRecord{
|
||||
RepositoryID: 1,
|
||||
ProjectID: 1,
|
||||
Name: "library/hello-world",
|
||||
}
|
||||
m.dao.On("NonEmptyRepos", mock.Anything).Return([]*models.RepoRecord{repository}, nil)
|
||||
repo, err := m.mgr.NonEmptyRepos(nil)
|
||||
m.Require().Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
m.Equal(repository.RepositoryID, repo[0].RepositoryID)
|
||||
}
|
||||
|
||||
func TestManager(t *testing.T) {
|
||||
suite.Run(t, &managerTestSuite{})
|
||||
}
|
||||
|
@ -15,14 +15,11 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"github.com/goharbor/harbor/src/controller/repository"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
lib_http "github.com/goharbor/harbor/src/lib/http"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
"github.com/goharbor/harbor/src/server/registry/util"
|
||||
"net/http"
|
||||
"sort"
|
||||
@ -31,14 +28,12 @@ import (
|
||||
|
||||
func newRepositoryHandler() http.Handler {
|
||||
return &repositoryHandler{
|
||||
repoCtl: repository.Ctl,
|
||||
artCtl: artifact.Ctl,
|
||||
repoMgr: repository.Mgr,
|
||||
}
|
||||
}
|
||||
|
||||
type repositoryHandler struct {
|
||||
repoCtl repository.Controller
|
||||
artCtl artifact.Controller
|
||||
repoMgr repository.Manager
|
||||
}
|
||||
|
||||
func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
@ -58,8 +53,8 @@ func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
}
|
||||
|
||||
repoNames := make([]string, 0)
|
||||
// get all repositories
|
||||
repoRecords, err := r.repoCtl.List(req.Context(), nil)
|
||||
// get all the non repositories
|
||||
repoRecords, err := r.repoMgr.NonEmptyRepos(req.Context())
|
||||
if err != nil {
|
||||
lib_http.SendError(w, err)
|
||||
return
|
||||
@ -69,14 +64,7 @@ func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
return
|
||||
}
|
||||
for _, repo := range repoRecords {
|
||||
valid, err := r.validateRepo(req.Context(), repo.RepositoryID)
|
||||
if err != nil {
|
||||
lib_http.SendError(w, err)
|
||||
return
|
||||
}
|
||||
if valid {
|
||||
repoNames = append(repoNames, repo.Name)
|
||||
}
|
||||
repoNames = append(repoNames, repo.Name)
|
||||
}
|
||||
sort.Strings(repoNames)
|
||||
if !withN {
|
||||
@ -140,34 +128,6 @@ func (r *repositoryHandler) sendResponse(w http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
}
|
||||
|
||||
// empty repo and all of artifacts are untagged should be filtered out.
|
||||
func (r *repositoryHandler) validateRepo(ctx context.Context, repositoryID int64) (bool, error) {
|
||||
arts, err := r.artCtl.List(ctx, &q.Query{
|
||||
Keywords: map[string]interface{}{
|
||||
"RepositoryID": repositoryID,
|
||||
},
|
||||
}, &artifact.Option{
|
||||
WithTag: true,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// empty repo
|
||||
if len(arts) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, art := range arts {
|
||||
if len(art.Tags) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// if all of artifact are untagged, filter out
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type catalogAPIResponse struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
@ -17,14 +17,9 @@ package registry
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"github.com/goharbor/harbor/src/controller/repository"
|
||||
"github.com/goharbor/harbor/src/controller/tag"
|
||||
pkg_art "github.com/goharbor/harbor/src/pkg/artifact"
|
||||
model_tag "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
artifacttesting "github.com/goharbor/harbor/src/testing/controller/artifact"
|
||||
repotesting "github.com/goharbor/harbor/src/testing/controller/repository"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
repotesting "github.com/goharbor/harbor/src/testing/pkg/repository"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -33,37 +28,31 @@ import (
|
||||
|
||||
type catalogTestSuite struct {
|
||||
suite.Suite
|
||||
originalRepoCtl repository.Controller
|
||||
originalArtCtl artifact.Controller
|
||||
repoCtl *repotesting.FakeController
|
||||
artCtl *artifacttesting.Controller
|
||||
originalRepoMgr repository.Manager
|
||||
repoMgr *repotesting.FakeManager
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) SetupSuite() {
|
||||
c.originalRepoCtl = repository.Ctl
|
||||
c.originalArtCtl = artifact.Ctl
|
||||
c.originalRepoMgr = repository.Mgr
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) SetupTest() {
|
||||
c.repoCtl = &repotesting.FakeController{}
|
||||
repository.Ctl = c.repoCtl
|
||||
c.artCtl = &artifacttesting.Controller{}
|
||||
artifact.Ctl = c.artCtl
|
||||
c.repoMgr = &repotesting.FakeManager{}
|
||||
repository.Mgr = c.repoMgr
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TearDownTest() {
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TearDownSuite() {
|
||||
repository.Ctl = c.originalRepoCtl
|
||||
artifact.Ctl = c.originalArtCtl
|
||||
repository.Mgr = c.originalRepoMgr
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalog() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
mock.OnAnything(c.repoMgr, "NonEmptyRepos").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
@ -73,22 +62,7 @@ func (c *catalogTestSuite) TestCatalog() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -102,10 +76,9 @@ func (c *catalogTestSuite) TestCatalog() {
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogPaginationN1() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog?n=1", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
c.repoMgr.On("NonEmptyRepos").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
@ -115,22 +88,6 @@ func (c *catalogTestSuite) TestCatalogPaginationN1() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -145,10 +102,9 @@ func (c *catalogTestSuite) TestCatalogPaginationN1() {
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogPaginationN2() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog?n=3", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
c.repoMgr.On("NonEmptyRepos").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
@ -158,22 +114,6 @@ func (c *catalogTestSuite) TestCatalogPaginationN2() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -188,10 +128,9 @@ func (c *catalogTestSuite) TestCatalogPaginationN2() {
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogPaginationN3() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog?last=busybox&n=1", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
c.repoMgr.On("NonEmptyRepos").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
@ -201,22 +140,6 @@ func (c *catalogTestSuite) TestCatalogPaginationN3() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -230,59 +153,10 @@ func (c *catalogTestSuite) TestCatalogPaginationN3() {
|
||||
c.Equal("hello-world", ctlg.Repositories[0])
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogUntaggedArtifact() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
},
|
||||
{
|
||||
RepositoryID: 2,
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
// untagged artifact
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
var ctlg struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
decoder := json.NewDecoder(w.Body)
|
||||
err := decoder.Decode(&ctlg)
|
||||
c.Nil(err)
|
||||
c.Equal(0, len(ctlg.Repositories))
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogEmptyRepo() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
},
|
||||
{
|
||||
RepositoryID: 2,
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
// empty repository
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{},
|
||||
}, nil)
|
||||
c.repoMgr.On("NonEmptyRepos").Return([]*models.RepoRecord{}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
|
@ -32,3 +32,4 @@ package pkg
|
||||
//go:generate mockery --case snake --dir ../../pkg/rbac/dao --name DAO --output ./rbac/dao --outpkg dao
|
||||
//go:generate mockery --case snake --dir ../../pkg/robot --name Manager --output ./robot --outpkg robot
|
||||
//go:generate mockery --case snake --dir ../../pkg/robot/dao --name DAO --output ./robot/dao --outpkg dao
|
||||
//go:generate mockery --case snake --dir ../../pkg/repository/dao --name DAO --output ./repository/dao --outpkg dao
|
||||
|
178
src/testing/pkg/repository/dao/dao.go
Normal file
178
src/testing/pkg/repository/dao/dao.go
Normal file
@ -0,0 +1,178 @@
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
models "github.com/goharbor/harbor/src/common/models"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
)
|
||||
|
||||
// DAO is an autogenerated mock type for the DAO type
|
||||
type DAO struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AddPullCount provides a mock function with given fields: ctx, id
|
||||
func (_m *DAO) AddPullCount(ctx context.Context, id int64) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Count provides a mock function with given fields: ctx, query
|
||||
func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, repository
|
||||
func (_m *DAO) Create(ctx context.Context, repository *models.RepoRecord) (int64, error) {
|
||||
ret := _m.Called(ctx, repository)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.RepoRecord) int64); ok {
|
||||
r0 = rf(ctx, repository)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *models.RepoRecord) error); ok {
|
||||
r1 = rf(ctx, repository)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
func (_m *DAO) Delete(ctx context.Context, id int64) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: ctx, id
|
||||
func (_m *DAO) Get(ctx context.Context, id int64) (*models.RepoRecord, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 *models.RepoRecord
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *models.RepoRecord); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.RepoRecord)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// List provides a mock function with given fields: ctx, query
|
||||
func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*models.RepoRecord, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 []*models.RepoRecord
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*models.RepoRecord); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.RepoRecord)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NonEmptyRepos provides a mock function with given fields: ctx
|
||||
func (_m *DAO) NonEmptyRepos(ctx context.Context) ([]*models.RepoRecord, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
var r0 []*models.RepoRecord
|
||||
if rf, ok := ret.Get(0).(func(context.Context) []*models.RepoRecord); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.RepoRecord)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, repository, props
|
||||
func (_m *DAO) Update(ctx context.Context, repository *models.RepoRecord, props ...string) error {
|
||||
_va := make([]interface{}, len(props))
|
||||
for _i := range props {
|
||||
_va[_i] = props[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, repository)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.RepoRecord, ...string) error); ok {
|
||||
r0 = rf(ctx, repository, props...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
@ -85,3 +85,13 @@ func (f *FakeManager) AddPullCount(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
// NonEmptyRepos ...
|
||||
func (f *FakeManager) NonEmptyRepos(ctx context.Context) ([]*models.RepoRecord, error) {
|
||||
args := f.Called()
|
||||
var repository []*models.RepoRecord
|
||||
if args.Get(0) != nil {
|
||||
repository = args.Get(0).([]*models.RepoRecord)
|
||||
}
|
||||
return repository, args.Error(1)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user