mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-17 04:11:24 +01:00
Merge pull request #16807 from chlins/feat/cache-layer-for-project
feat(project): introduce cache manager for project (#16740)
This commit is contained in:
commit
f78047f169
@ -1,11 +1,12 @@
|
|||||||
package immutable
|
package immutable
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/utils/test"
|
"github.com/goharbor/harbor/src/common/utils/test"
|
||||||
"github.com/goharbor/harbor/src/pkg/immutable/model"
|
"github.com/goharbor/harbor/src/pkg/immutable/model"
|
||||||
@ -39,12 +40,12 @@ func (s *ControllerTestSuite) TestImmutableRule() {
|
|||||||
var err error
|
var err error
|
||||||
ctx := s.Context()
|
ctx := s.Context()
|
||||||
|
|
||||||
projectID, err := project.Mgr.Create(ctx, &proModels.Project{
|
projectID, err := pkg.ProjectMgr.Create(ctx, &proModels.Project{
|
||||||
Name: "testimmutablerule",
|
Name: "testimmutablerule",
|
||||||
OwnerID: 1,
|
OwnerID: 1,
|
||||||
})
|
})
|
||||||
if s.Nil(err) {
|
if s.Nil(err) {
|
||||||
defer project.Mgr.Delete(ctx, projectID)
|
defer pkg.ProjectMgr.Delete(ctx, projectID)
|
||||||
}
|
}
|
||||||
|
|
||||||
rule := &model.Metadata{
|
rule := &model.Metadata{
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/core/auth"
|
"github.com/goharbor/harbor/src/core/auth"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/member"
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
"github.com/goharbor/harbor/src/pkg/member/models"
|
"github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
@ -81,7 +82,7 @@ type controller struct {
|
|||||||
|
|
||||||
// NewController ...
|
// NewController ...
|
||||||
func NewController() Controller {
|
func NewController() Controller {
|
||||||
return &controller{mgr: member.Mgr, projectMgr: project.Mgr, userManager: user.New()}
|
return &controller{mgr: member.Mgr, projectMgr: pkg.ProjectMgr, userManager: user.New()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) Count(ctx context.Context, projectNameOrID interface{}, query *q.Query) (int, error) {
|
func (c *controller) Count(ctx context.Context, projectNameOrID interface{}, query *q.Query) (int, error) {
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/allowlist"
|
"github.com/goharbor/harbor/src/pkg/allowlist"
|
||||||
"github.com/goharbor/harbor/src/pkg/notification"
|
"github.com/goharbor/harbor/src/pkg/notification"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
@ -68,7 +69,7 @@ type Controller interface {
|
|||||||
// NewController creates an instance of the default project controller
|
// NewController creates an instance of the default project controller
|
||||||
func NewController() Controller {
|
func NewController() Controller {
|
||||||
return &controller{
|
return &controller{
|
||||||
projectMgr: project.Mgr,
|
projectMgr: pkg.ProjectMgr,
|
||||||
metaMgr: metadata.Mgr,
|
metaMgr: metadata.Mgr,
|
||||||
allowlistMgr: allowlist.NewDefaultManager(),
|
allowlistMgr: allowlist.NewDefaultManager(),
|
||||||
userMgr: user.Mgr,
|
userMgr: user.Mgr,
|
||||||
|
@ -16,13 +16,14 @@ package project
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
|
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/user"
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
"github.com/graph-gophers/dataloader"
|
"github.com/graph-gophers/dataloader"
|
||||||
)
|
)
|
||||||
@ -45,7 +46,7 @@ func getProjectsBatchFn(ctx context.Context, keys dataloader.Keys) []*dataloader
|
|||||||
projectIDs = append(projectIDs, id)
|
projectIDs = append(projectIDs, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
projects, err := project.Mgr.List(ctx, q.New(q.KeyWords{"project_id__in": projectIDs}))
|
projects, err := pkg.ProjectMgr.List(ctx, q.New(q.KeyWords{"project_id__in": projectIDs}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleError(err)
|
return handleError(err)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
"github.com/goharbor/harbor/src/pkg/reg"
|
"github.com/goharbor/harbor/src/pkg/reg"
|
||||||
"github.com/goharbor/harbor/src/pkg/reg/model"
|
"github.com/goharbor/harbor/src/pkg/reg/model"
|
||||||
@ -64,7 +65,7 @@ func NewController() Controller {
|
|||||||
return &controller{
|
return &controller{
|
||||||
regMgr: reg.Mgr,
|
regMgr: reg.Mgr,
|
||||||
repMgr: replication.Mgr,
|
repMgr: replication.Mgr,
|
||||||
proMgr: project.Mgr,
|
proMgr: pkg.ProjectMgr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ type Controller interface {
|
|||||||
// NewController creates an instance of the default repository controller
|
// NewController creates an instance of the default repository controller
|
||||||
func NewController() Controller {
|
func NewController() Controller {
|
||||||
return &controller{
|
return &controller{
|
||||||
proMgr: project.Mgr,
|
proMgr: pkg.ProjectMgr,
|
||||||
repoMgr: repository.Mgr,
|
repoMgr: repository.Mgr,
|
||||||
artMgr: pkg.ArtifactMgr,
|
artMgr: pkg.ArtifactMgr,
|
||||||
artCtl: artifact.Ctl,
|
artCtl: artifact.Ctl,
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"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/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
"github.com/goharbor/harbor/src/pkg/repository"
|
"github.com/goharbor/harbor/src/pkg/repository"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention"
|
"github.com/goharbor/harbor/src/pkg/retention"
|
||||||
@ -390,13 +391,13 @@ func (r *defaultController) UpdateTaskInfo(ctx context.Context, taskID int64, to
|
|||||||
// NewController ...
|
// NewController ...
|
||||||
func NewController() Controller {
|
func NewController() Controller {
|
||||||
retentionMgr := retention.NewManager()
|
retentionMgr := retention.NewManager()
|
||||||
retentionLauncher := retention.NewLauncher(project.Mgr, repository.Mgr, retentionMgr, task.ExecMgr, task.Mgr)
|
retentionLauncher := retention.NewLauncher(pkg.ProjectMgr, repository.Mgr, retentionMgr, task.ExecMgr, task.Mgr)
|
||||||
return &defaultController{
|
return &defaultController{
|
||||||
manager: retentionMgr,
|
manager: retentionMgr,
|
||||||
execMgr: task.ExecMgr,
|
execMgr: task.ExecMgr,
|
||||||
taskMgr: task.Mgr,
|
taskMgr: task.Mgr,
|
||||||
launcher: retentionLauncher,
|
launcher: retentionLauncher,
|
||||||
projectManager: project.Mgr,
|
projectManager: pkg.ProjectMgr,
|
||||||
repositoryMgr: repository.Mgr,
|
repositoryMgr: repository.Mgr,
|
||||||
scheduler: scheduler.Sched,
|
scheduler: scheduler.Sched,
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
"github.com/goharbor/harbor/src/pkg/rbac"
|
"github.com/goharbor/harbor/src/pkg/rbac"
|
||||||
@ -57,7 +58,7 @@ type controller struct {
|
|||||||
func NewController() Controller {
|
func NewController() Controller {
|
||||||
return &controller{
|
return &controller{
|
||||||
robotMgr: robot.Mgr,
|
robotMgr: robot.Mgr,
|
||||||
proMgr: project.Mgr,
|
proMgr: pkg.ProjectMgr,
|
||||||
rbacMgr: rbac.Mgr,
|
rbacMgr: rbac.Mgr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,11 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/config"
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
||||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||||
"github.com/goharbor/harbor/src/pkg/member"
|
"github.com/goharbor/harbor/src/pkg/member"
|
||||||
memberModels "github.com/goharbor/harbor/src/pkg/member/models"
|
memberModels "github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
|
||||||
userpkg "github.com/goharbor/harbor/src/pkg/user"
|
userpkg "github.com/goharbor/harbor/src/pkg/user"
|
||||||
userDao "github.com/goharbor/harbor/src/pkg/user/dao"
|
userDao "github.com/goharbor/harbor/src/pkg/user/dao"
|
||||||
"github.com/goharbor/harbor/src/pkg/usergroup"
|
"github.com/goharbor/harbor/src/pkg/usergroup"
|
||||||
@ -382,7 +382,7 @@ func TestSearchAndOnBoardUser(t *testing.T) {
|
|||||||
func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
||||||
memberMgr := member.Mgr
|
memberMgr := member.Mgr
|
||||||
ctx := orm.Context()
|
ctx := orm.Context()
|
||||||
currentProject, err := project.Mgr.Get(ctx, "member_test_01")
|
currentProject, err := pkg.ProjectMgr.Get(ctx, "member_test_01")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||||
}
|
}
|
||||||
@ -402,7 +402,7 @@ func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
|||||||
t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid:%v", pmid)
|
t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid:%v", pmid)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProject, err = project.Mgr.Get(ctx, "member_test_02")
|
currentProject, err = pkg.ProjectMgr.Get(ctx, "member_test_02")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||||
}
|
}
|
||||||
@ -424,7 +424,7 @@ func TestAddProjectMemberWithLdapUser(t *testing.T) {
|
|||||||
func TestAddProjectMemberWithLdapGroup(t *testing.T) {
|
func TestAddProjectMemberWithLdapGroup(t *testing.T) {
|
||||||
memberMgr := member.Mgr
|
memberMgr := member.Mgr
|
||||||
ctx := orm.Context()
|
ctx := orm.Context()
|
||||||
currentProject, err := project.Mgr.Get(ctx, "member_test_01")
|
currentProject, err := pkg.ProjectMgr.Get(ctx, "member_test_01")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
t.Errorf("Error occurred when GetProjectByName: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,12 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/repository"
|
"github.com/goharbor/harbor/src/pkg/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
func abstractArtData(ctx context.Context) error {
|
func abstractArtData(ctx context.Context) error {
|
||||||
abstractor := art.NewAbstractor()
|
abstractor := art.NewAbstractor()
|
||||||
pros, err := project.Mgr.List(ctx, nil)
|
pros, err := pkg.ProjectMgr.List(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ const (
|
|||||||
// Resource type definitions
|
// Resource type definitions
|
||||||
// ResourceTypeArtifact defines artifact type.
|
// ResourceTypeArtifact defines artifact type.
|
||||||
ResourceTypeArtifact = "artifact"
|
ResourceTypeArtifact = "artifact"
|
||||||
|
// ResourceTypeProject defines project type.
|
||||||
|
ResourceTypeProject = "project"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manager is the interface for resource cache manager.
|
// Manager is the interface for resource cache manager.
|
||||||
|
202
src/pkg/cached/project/redis/manager.go
Normal file
202
src/pkg/cached/project/redis/manager.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
// 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 redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
|
libcache "github.com/goharbor/harbor/src/lib/cache"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/lib/retry"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/cached"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ CachedManager = &manager{}
|
||||||
|
|
||||||
|
// CachedManager is the interface combines raw resource manager and cached manager for better extension.
|
||||||
|
type CachedManager interface {
|
||||||
|
// Manager is the raw resource manager.
|
||||||
|
project.Manager
|
||||||
|
// Manager is the common interface for resource cache.
|
||||||
|
cached.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// manager is the cached manager implemented by redis.
|
||||||
|
type manager struct {
|
||||||
|
// delegator delegates the raw crud to DAO.
|
||||||
|
delegator project.Manager
|
||||||
|
// client returns the redis cache client.
|
||||||
|
client func() libcache.Cache
|
||||||
|
// keyBuilder builds cache object key.
|
||||||
|
keyBuilder *cached.ObjectKey
|
||||||
|
// lifetime is the cache life time.
|
||||||
|
lifetime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManager returns the redis cache manager.
|
||||||
|
func NewManager(m project.Manager) *manager {
|
||||||
|
return &manager{
|
||||||
|
delegator: m,
|
||||||
|
client: func() libcache.Cache { return libcache.Default() },
|
||||||
|
keyBuilder: cached.NewObjectKey(cached.ResourceTypeProject),
|
||||||
|
lifetime: time.Duration(config.CacheExpireHours()) * time.Hour,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) Create(ctx context.Context, project *models.Project) (int64, error) {
|
||||||
|
return m.delegator.Create(ctx, project)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) Count(ctx context.Context, query *q.Query) (total int64, err error) {
|
||||||
|
return m.delegator.Count(ctx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) List(ctx context.Context, query *q.Query) ([]*models.Project, error) {
|
||||||
|
return m.delegator.List(ctx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) ListRoles(ctx context.Context, projectID int64, userID int, groupIDs ...int) ([]int, error) {
|
||||||
|
return m.delegator.ListRoles(ctx, projectID, userID, groupIDs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) Delete(ctx context.Context, id int64) error {
|
||||||
|
p, err := m.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass on delete operation
|
||||||
|
if err := m.delegator.Delete(ctx, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// clean cache
|
||||||
|
m.cleanUp(ctx, p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) Get(ctx context.Context, idOrName interface{}) (*models.Project, error) {
|
||||||
|
var (
|
||||||
|
key string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
id, name, err := utils.ParseProjectIDOrName(idOrName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if id != 0 {
|
||||||
|
key, err = m.keyBuilder.Format("id", id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
key, err = m.keyBuilder.Format("name", name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &models.Project{}
|
||||||
|
if err = m.client().Fetch(ctx, key, p); err == nil {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("get project %v from cache error: %v, will query from database.", idOrName, err)
|
||||||
|
|
||||||
|
p, err = m.delegator.Get(ctx, idOrName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = m.client().Save(ctx, key, p, m.lifetime); err != nil {
|
||||||
|
// log error if save to cache failed
|
||||||
|
log.Debugf("save project %s to cache error: %v", p.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanUp cleans up data in cache.
|
||||||
|
func (m *manager) cleanUp(ctx context.Context, p *models.Project) {
|
||||||
|
// clean index by id
|
||||||
|
idIdx, err := m.keyBuilder.Format("id", p.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("format project id key error: %v", err)
|
||||||
|
} else {
|
||||||
|
// retry to avoid dirty data
|
||||||
|
if err = retry.Retry(func() error { return m.client().Delete(ctx, idIdx) }); err != nil {
|
||||||
|
log.Errorf("delete project cache key %s error: %v", idIdx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean index by name
|
||||||
|
nameIdx, err := m.keyBuilder.Format("name", p.Name)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("format project name key error: %v", err)
|
||||||
|
} else {
|
||||||
|
if err = retry.Retry(func() error { return m.client().Delete(ctx, nameIdx) }); err != nil {
|
||||||
|
log.Errorf("delete project cache key %s error: %v", nameIdx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) ResourceType(ctx context.Context) string {
|
||||||
|
return cached.ResourceTypeProject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) CountCache(ctx context.Context) (int64, error) {
|
||||||
|
// prefix is resource type
|
||||||
|
keys, err := m.client().Keys(ctx, m.ResourceType(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(len(keys)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) DeleteCache(ctx context.Context, key string) error {
|
||||||
|
return m.client().Delete(ctx, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *manager) FlushAll(ctx context.Context) error {
|
||||||
|
// prefix is resource type
|
||||||
|
keys, err := m.client().Keys(ctx, m.ResourceType(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs errors.Errors
|
||||||
|
for _, key := range keys {
|
||||||
|
if err = m.client().Delete(ctx, key); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs.Len() > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
139
src/pkg/cached/project/redis/manager_test.go
Normal file
139
src/pkg/cached/project/redis/manager_test.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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 redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/lib/cache"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
|
testcache "github.com/goharbor/harbor/src/testing/lib/cache"
|
||||||
|
"github.com/goharbor/harbor/src/testing/mock"
|
||||||
|
testProject "github.com/goharbor/harbor/src/testing/pkg/project"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type managerTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
cachedManager CachedManager
|
||||||
|
projectMgr *testProject.Manager
|
||||||
|
cache *testcache.Cache
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) SetupTest() {
|
||||||
|
m.projectMgr = &testProject.Manager{}
|
||||||
|
m.cache = &testcache.Cache{}
|
||||||
|
m.cachedManager = NewManager(
|
||||||
|
m.projectMgr,
|
||||||
|
)
|
||||||
|
m.cachedManager.(*manager).client = func() cache.Cache { return m.cache }
|
||||||
|
m.ctx = context.TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestCreate() {
|
||||||
|
m.projectMgr.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||||
|
id, err := m.cachedManager.Create(m.ctx, &models.Project{})
|
||||||
|
m.NoError(err)
|
||||||
|
m.Equal(int64(1), id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestCount() {
|
||||||
|
m.projectMgr.On("Count", mock.Anything, mock.Anything).Return(int64(100), nil)
|
||||||
|
c, err := m.cachedManager.Count(m.ctx, q.New(q.KeyWords{}))
|
||||||
|
m.NoError(err)
|
||||||
|
m.Equal(int64(100), c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) List() {
|
||||||
|
m.projectMgr.On("List", mock.Anything, mock.Anything).Return([]*models.Project{}, nil)
|
||||||
|
ps, err := m.cachedManager.List(m.ctx, q.New(q.KeyWords{}))
|
||||||
|
m.NoError(err)
|
||||||
|
m.ElementsMatch([]*models.Project{}, ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) ListRoles() {
|
||||||
|
m.projectMgr.On("ListRoles", mock.Anything, mock.Anything, mock.Anything).Return([]int{1}, nil)
|
||||||
|
rs, err := m.cachedManager.ListRoles(m.ctx, 1, 1)
|
||||||
|
m.NoError(err)
|
||||||
|
m.Equal([]int{1}, rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestGet() {
|
||||||
|
// get from cache directly
|
||||||
|
m.cache.On("Fetch", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
_, err := m.cachedManager.Get(m.ctx, 100)
|
||||||
|
m.NoError(err, "should get from cache")
|
||||||
|
m.projectMgr.AssertNotCalled(m.T(), "Get", mock.Anything, mock.Anything)
|
||||||
|
|
||||||
|
// not found in cache, read from dao
|
||||||
|
m.cache.On("Fetch", mock.Anything, mock.Anything, mock.Anything).Return(cache.ErrNotFound).Once()
|
||||||
|
m.cache.On("Save", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
m.projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{}, nil).Once()
|
||||||
|
_, err = m.cachedManager.Get(m.ctx, 100)
|
||||||
|
m.NoError(err, "should get from projectMgr")
|
||||||
|
m.projectMgr.AssertCalled(m.T(), "Get", mock.Anything, mock.Anything)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestDelete() {
|
||||||
|
// delete from projectMgr error
|
||||||
|
errDelete := errors.New("delete failed")
|
||||||
|
m.projectMgr.On("Delete", mock.Anything, mock.Anything).Return(errDelete).Once()
|
||||||
|
m.cache.On("Fetch", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
err := m.cachedManager.Delete(m.ctx, 100)
|
||||||
|
m.ErrorIs(err, errDelete, "delete should error")
|
||||||
|
m.cache.AssertNotCalled(m.T(), "Delete", mock.Anything, mock.Anything)
|
||||||
|
|
||||||
|
// delete from projectMgr success
|
||||||
|
m.projectMgr.On("Delete", mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
m.cache.On("Fetch", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
m.cache.On("Delete", mock.Anything, mock.Anything).Return(nil).Twice()
|
||||||
|
err = m.cachedManager.Delete(m.ctx, 100)
|
||||||
|
m.NoError(err, "delete should success")
|
||||||
|
m.cache.AssertCalled(m.T(), "Delete", mock.Anything, mock.Anything)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestResourceType() {
|
||||||
|
t := m.cachedManager.ResourceType(m.ctx)
|
||||||
|
m.Equal("project", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestCountCache() {
|
||||||
|
m.cache.On("Keys", mock.Anything, mock.Anything).Return([]string{"1"}, nil).Once()
|
||||||
|
c, err := m.cachedManager.CountCache(m.ctx)
|
||||||
|
m.NoError(err)
|
||||||
|
m.Equal(int64(1), c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestDeleteCache() {
|
||||||
|
m.cache.On("Delete", mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
err := m.cachedManager.DeleteCache(m.ctx, "key")
|
||||||
|
m.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerTestSuite) TestFlushAll() {
|
||||||
|
m.cache.On("Keys", mock.Anything, mock.Anything).Return([]string{"1"}, nil).Once()
|
||||||
|
m.cache.On("Delete", mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
err := m.cachedManager.FlushAll(m.ctx)
|
||||||
|
m.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManager(t *testing.T) {
|
||||||
|
suite.Run(t, &managerTestSuite{})
|
||||||
|
}
|
@ -15,35 +15,45 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
"github.com/goharbor/harbor/src/pkg/cached/artifact/redis"
|
cachedArtifact "github.com/goharbor/harbor/src/pkg/cached/artifact/redis"
|
||||||
|
cachedProject "github.com/goharbor/harbor/src/pkg/cached/project/redis"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define global resource manager.
|
// Define global resource manager.
|
||||||
var (
|
var (
|
||||||
// once for only init one time.
|
|
||||||
once sync.Once
|
|
||||||
// ArtifactMgr is the manager for artifact.
|
// ArtifactMgr is the manager for artifact.
|
||||||
ArtifactMgr artifact.Manager
|
ArtifactMgr artifact.Manager
|
||||||
|
// ProjectMgr is the manager for project.
|
||||||
|
ProjectMgr project.Manager
|
||||||
)
|
)
|
||||||
|
|
||||||
// init initialize mananger for resources
|
// init initialize mananger for resources
|
||||||
func init() {
|
func init() {
|
||||||
once.Do(func() {
|
cacheEnabled := config.CacheEnabled()
|
||||||
cacheEnabled := config.CacheEnabled()
|
initArtifactMgr(cacheEnabled)
|
||||||
initArtifactManager(cacheEnabled)
|
initProjectMgr(cacheEnabled)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initArtifactManager(cacheEnabled bool) {
|
func initArtifactMgr(cacheEnabled bool) {
|
||||||
artMgr := artifact.NewManager()
|
artMgr := artifact.NewManager()
|
||||||
// check cache enable
|
// check cache enable
|
||||||
if cacheEnabled {
|
if cacheEnabled {
|
||||||
ArtifactMgr = redis.NewManager(artMgr)
|
ArtifactMgr = cachedArtifact.NewManager(artMgr)
|
||||||
} else {
|
} else {
|
||||||
ArtifactMgr = artMgr
|
ArtifactMgr = artMgr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initProjectMgr(cacheEnabled bool) {
|
||||||
|
projectMgr := project.New()
|
||||||
|
// check cache enable
|
||||||
|
if cacheEnabled {
|
||||||
|
ProjectMgr = cachedProject.NewManager(projectMgr)
|
||||||
|
} else {
|
||||||
|
ProjectMgr = projectMgr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,19 +18,30 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||||
"github.com/goharbor/harbor/src/pkg/cached/artifact/redis"
|
cachedArtifact "github.com/goharbor/harbor/src/pkg/cached/artifact/redis"
|
||||||
|
cachedProject "github.com/goharbor/harbor/src/pkg/cached/project/redis"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInitArtifactMgr(t *testing.T) {
|
||||||
// cache not enable
|
// cache not enable
|
||||||
// artifact
|
|
||||||
assert.NotNil(t, ArtifactMgr)
|
assert.NotNil(t, ArtifactMgr)
|
||||||
assert.IsType(t, artifact.NewManager(), ArtifactMgr)
|
assert.IsType(t, artifact.NewManager(), ArtifactMgr)
|
||||||
|
|
||||||
// cache enable
|
// cache enable
|
||||||
initArtifactManager(true)
|
initArtifactMgr(true)
|
||||||
// artifact
|
|
||||||
assert.NotNil(t, ArtifactMgr)
|
assert.NotNil(t, ArtifactMgr)
|
||||||
assert.IsType(t, redis.NewManager(artifact.NewManager()), ArtifactMgr)
|
assert.IsType(t, cachedArtifact.NewManager(artifact.NewManager()), ArtifactMgr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitProjectMgr(t *testing.T) {
|
||||||
|
// cache not enable
|
||||||
|
assert.NotNil(t, ProjectMgr)
|
||||||
|
assert.IsType(t, project.New(), ProjectMgr)
|
||||||
|
|
||||||
|
// cache enable
|
||||||
|
initProjectMgr(true)
|
||||||
|
assert.NotNil(t, ProjectMgr)
|
||||||
|
assert.IsType(t, cachedProject.NewManager(project.New()), ProjectMgr)
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,13 @@ package dao
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
_ "github.com/goharbor/harbor/src/common/dao"
|
_ "github.com/goharbor/harbor/src/common/dao"
|
||||||
testDao "github.com/goharbor/harbor/src/common/dao"
|
testDao "github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/member/models"
|
"github.com/goharbor/harbor/src/pkg/member/models"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
"github.com/goharbor/harbor/src/pkg/user"
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
@ -28,7 +31,6 @@ import (
|
|||||||
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
||||||
htesting "github.com/goharbor/harbor/src/testing"
|
htesting "github.com/goharbor/harbor/src/testing"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DaoTestSuite struct {
|
type DaoTestSuite struct {
|
||||||
@ -67,7 +69,7 @@ func (s *DaoTestSuite) SetupSuite() {
|
|||||||
"delete from project_member where id > 1",
|
"delete from project_member where id > 1",
|
||||||
}
|
}
|
||||||
testDao.PrepareTestData(clearSqls, initSqls)
|
testDao.PrepareTestData(clearSqls, initSqls)
|
||||||
s.projectMgr = project.Mgr
|
s.projectMgr = pkg.ProjectMgr
|
||||||
s.userMgr = user.Mgr
|
s.userMgr = user.Mgr
|
||||||
ctx := s.Context()
|
ctx := s.Context()
|
||||||
proj, err := s.projectMgr.Get(ctx, "member_test_01")
|
proj, err := s.projectMgr.Get(ctx, "member_test_01")
|
||||||
|
@ -26,11 +26,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/pkg/project/models"
|
"github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// Mgr is the global project manager
|
|
||||||
Mgr = New()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Manager is used for project management
|
// Manager is used for project management
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
// Create create project instance
|
// Create create project instance
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/immutable"
|
"github.com/goharbor/harbor/src/controller/immutable"
|
||||||
"github.com/goharbor/harbor/src/pkg"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
|
||||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
@ -75,7 +74,7 @@ func randomString(n int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *HandlerSuite) addProject(ctx context.Context, projectName string) int64 {
|
func (suite *HandlerSuite) addProject(ctx context.Context, projectName string) int64 {
|
||||||
projectID, err := project.Mgr.Create(ctx, &proModels.Project{
|
projectID, err := pkg.ProjectMgr.Create(ctx, &proModels.Project{
|
||||||
Name: projectName,
|
Name: projectName,
|
||||||
OwnerID: 1,
|
OwnerID: 1,
|
||||||
})
|
})
|
||||||
@ -163,7 +162,7 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
|
|||||||
tagID := suite.addTags(ctx, repoID, afID, "release-1.10")
|
tagID := suite.addTags(ctx, repoID, afID, "release-1.10")
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
project.Mgr.Delete(ctx, projectID)
|
pkg.ProjectMgr.Delete(ctx, projectID)
|
||||||
pkg.ArtifactMgr.Delete(ctx, afID)
|
pkg.ArtifactMgr.Delete(ctx, afID)
|
||||||
repository.Mgr.Delete(ctx, repoID)
|
repository.Mgr.Delete(ctx, repoID)
|
||||||
tag.Mgr.Delete(ctx, tagID)
|
tag.Mgr.Delete(ctx, tagID)
|
||||||
|
Loading…
Reference in New Issue
Block a user