Merge pull request #10561 from ywk253100/200120_artifact_controller

Implement the get/delete handler for registry API
This commit is contained in:
Wenkai Yin(尹文开) 2020-01-29 12:33:43 +08:00 committed by GitHub
commit dee70f7deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 332 additions and 112 deletions

View File

@ -18,9 +18,14 @@ import (
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
"github.com/goharbor/harbor/src/common/utils/registry"
"github.com/goharbor/harbor/src/common/utils/registry/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/service/token"
coreutils "github.com/goharbor/harbor/src/core/utils"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"io/ioutil"
"net/http"
)
var (
@ -54,7 +59,7 @@ type fetcher struct{}
// TODO re-implement it based on OCI registry driver
func (f *fetcher) FetchManifest(repository, digest string) (string, []byte, error) {
// TODO read from cache first
client, err := coreutils.NewRepositoryClientForLocal("admin", repository)
client, err := newRepositoryClient(repository)
if err != nil {
return "", nil, err
}
@ -76,3 +81,19 @@ func (f *fetcher) FetchLayer(repository, digest string) ([]byte, error) {
defer reader.Close()
return ioutil.ReadAll(reader)
}
func newRepositoryClient(repository string) (*registry.Repository, error) {
uam := &auth.UserAgentModifier{
UserAgent: "harbor-registry-client",
}
authorizer := auth.NewRawTokenAuthorizer("admin", token.Registry)
transport := registry.NewTransport(http.DefaultTransport, authorizer, uam)
client := &http.Client{
Transport: transport,
}
endpoint, err := config.RegistryURL()
if err != nil {
return nil, err
}
return registry.NewRepository(repository, endpoint, client)
}

View File

@ -18,7 +18,8 @@ import (
"context"
"fmt"
"github.com/goharbor/harbor/src/api/artifact/abstractor"
// register image resolvers
"github.com/opencontainers/go-digest"
// registry image resolvers
_ "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver/image"
// register chart resolver
_ "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver/chart"
@ -48,6 +49,9 @@ type Controller interface {
List(ctx context.Context, query *q.Query, option *Option) (total int64, artifacts []*Artifact, err error)
// Get the artifact specified by ID, specify the properties returned with option
Get(ctx context.Context, id int64, option *Option) (artifact *Artifact, err error)
// Get the artifact specified by repository name and reference, the reference can be tag or digest,
// specify the properties returned with option
GetByReference(ctx context.Context, repository, reference string, option *Option) (artifact *Artifact, err error)
// Delete the artifact specified by ID. All tags attached to the artifact are deleted as well
Delete(ctx context.Context, id int64) (err error)
// Tags returns the tags according to the query, specify the properties returned with option
@ -207,12 +211,58 @@ func (c *controller) Get(ctx context.Context, id int64, option *Option) (*Artifa
return c.assembleArtifact(ctx, art, option), nil
}
func (c *controller) Delete(ctx context.Context, id int64) error {
// delete artifact first in case the artifact is referenced by other artifact
if err := c.artMgr.Delete(ctx, id); err != nil {
return err
func (c *controller) GetByReference(ctx context.Context, repository, reference string, option *Option) (*Artifact, error) {
// the reference is tag
if _, err := digest.Parse(reference); err != nil {
return c.getByTag(ctx, repository, reference, option)
}
// the reference is digest
return c.getByDigest(ctx, repository, reference, option)
}
func (c *controller) getByDigest(ctx context.Context, repository, digest string, option *Option) (*Artifact, error) {
repo, err := c.repoMgr.GetByName(ctx, repository)
if err != nil {
return nil, err
}
_, artifacts, err := c.List(ctx, &q.Query{
Keywords: map[string]interface{}{
"RepositoryID": repo.RepositoryID,
"Digest": digest,
},
}, option)
if err != nil {
return nil, err
}
if len(artifacts) == 0 {
return nil, ierror.New(nil).WithCode(ierror.NotFoundCode).
WithMessage("artifact %s@%s not found", repository, digest)
}
return artifacts[0], nil
}
func (c *controller) getByTag(ctx context.Context, repository, tag string, option *Option) (*Artifact, error) {
repo, err := c.repoMgr.GetByName(ctx, repository)
if err != nil {
return nil, err
}
_, tags, err := c.tagMgr.List(ctx, &q.Query{
Keywords: map[string]interface{}{
"RepositoryID": repo.RepositoryID,
"Name": tag,
},
})
if err != nil {
return nil, err
}
if len(tags) == 0 {
return nil, ierror.New(nil).WithCode(ierror.NotFoundCode).
WithMessage("artifact %s:%s not found", repository, tag)
}
return c.Get(ctx, tags[0].ArtifactID, option)
}
func (c *controller) Delete(ctx context.Context, id int64) error {
// delete all tags that attached to the artifact
_, tags, err := c.tagMgr.List(ctx, &q.Query{
Keywords: map[string]interface{}{
@ -227,6 +277,11 @@ func (c *controller) Delete(ctx context.Context, id int64) error {
return err
}
}
if err := c.artMgr.Delete(ctx, id); err != nil {
return err
}
// TODO fire delete artifact event
return nil
}

View File

@ -17,6 +17,7 @@ package artifact
import (
"context"
"github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
@ -250,6 +251,126 @@ func (c *controllerTestSuite) TestGet() {
c.Equal(int64(1), art.ID)
}
func (c *controllerTestSuite) TestGetByDigest() {
// not found
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
c.artMgr.On("List").Return(0, nil, nil)
art, err := c.ctl.getByDigest(nil, "library/hello-world",
"sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", nil)
c.Require().NotNil(err)
c.repoMgr.AssertExpectations(c.T())
c.artMgr.AssertExpectations(c.T())
c.True(ierror.IsErr(err, ierror.NotFoundCode))
// reset the mock
c.SetupTest()
// success
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
c.artMgr.On("List").Return(1, []*artifact.Artifact{
{
ID: 1,
RepositoryID: 1,
},
}, nil)
art, err = c.ctl.getByDigest(nil, "library/hello-world",
"sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", nil)
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.artMgr.AssertExpectations(c.T())
c.Require().NotNil(art)
c.Equal(int64(1), art.ID)
}
func (c *controllerTestSuite) TestGetByTag() {
// not found
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
c.tagMgr.On("List").Return(0, nil, nil)
art, err := c.ctl.getByTag(nil, "library/hello-world", "latest", nil)
c.Require().NotNil(err)
c.repoMgr.AssertExpectations(c.T())
c.tagMgr.AssertExpectations(c.T())
c.True(ierror.IsErr(err, ierror.NotFoundCode))
// reset the mock
c.SetupTest()
// success
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
c.tagMgr.On("List").Return(1, []*tag.Tag{
{
ID: 1,
RepositoryID: 1,
Name: "latest",
ArtifactID: 1,
},
}, nil)
c.artMgr.On("Get").Return(&artifact.Artifact{
ID: 1,
}, nil)
art, err = c.ctl.getByTag(nil, "library/hello-world", "latest", nil)
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.tagMgr.AssertExpectations(c.T())
c.artMgr.AssertExpectations(c.T())
c.Require().NotNil(art)
c.Equal(int64(1), art.ID)
}
func (c *controllerTestSuite) TestGetByReference() {
// reference is digest
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
c.artMgr.On("List").Return(1, []*artifact.Artifact{
{
ID: 1,
RepositoryID: 1,
},
}, nil)
art, err := c.ctl.GetByReference(nil, "library/hello-world",
"sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", nil)
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.artMgr.AssertExpectations(c.T())
c.Require().NotNil(art)
c.Equal(int64(1), art.ID)
// reset the mock
c.SetupTest()
// reference is tag
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
c.tagMgr.On("List").Return(1, []*tag.Tag{
{
ID: 1,
RepositoryID: 1,
Name: "latest",
ArtifactID: 1,
},
}, nil)
c.artMgr.On("Get").Return(&artifact.Artifact{
ID: 1,
}, nil)
art, err = c.ctl.GetByReference(nil, "library/hello-world", "latest", nil)
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.tagMgr.AssertExpectations(c.T())
c.artMgr.AssertExpectations(c.T())
c.Require().NotNil(art)
c.Equal(int64(1), art.ID)
}
func (c *controllerTestSuite) TestDelete() {
c.artMgr.On("Delete").Return(nil)
c.tagMgr.On("List").Return(0, []*tag.Tag{

View File

@ -17,38 +17,42 @@ package repository
import (
"context"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/repository"
)
var (
// Ctl is a global repository controller instance
Ctl = NewController(repository.Mgr)
Ctl = NewController()
)
// Controller defines the operations related with repositories
type Controller interface {
// Ensure the repository specified by the "name" exists under the project,
// creates it if it doesn't exist. The "name" should contain the namespace part.
// The "created" will be set as true when the repository is created
Ensure(ctx context.Context, projectID int64, name string) (created bool, id int64, err error)
// Ensure the repository specified by the "name" exists, creates it if it doesn't exist.
// The "name" should contain the namespace part. The "created" will be set as true
// when the repository is created
Ensure(ctx context.Context, name string) (created bool, id int64, err error)
// Get the repository specified by ID
Get(ctx context.Context, id int64) (repository *models.RepoRecord, err error)
}
// NewController creates an instance of the default repository controller
func NewController(repoMgr repository.Manager) Controller {
func NewController() Controller {
return &controller{
repoMgr: repoMgr,
proMgr: project.Mgr,
repoMgr: repository.Mgr,
}
}
type controller struct {
proMgr project.Manager
repoMgr repository.Manager
}
func (c *controller) Ensure(ctx context.Context, projectID int64, name string) (bool, int64, error) {
func (c *controller) Ensure(ctx context.Context, name string) (bool, int64, error) {
query := &q.Query{
Keywords: map[string]interface{}{
"name": name,
@ -64,8 +68,13 @@ func (c *controller) Ensure(ctx context.Context, projectID int64, name string) (
}
// the repository doesn't exist, create it first
projectName, _ := utils.ParseRepository(name)
project, err := c.proMgr.Get(projectName)
if err != nil {
return false, 0, err
}
id, err := c.repoMgr.Create(ctx, &models.RepoRecord{
ProjectID: projectID,
ProjectID: project.ProjectID,
Name: name,
})
if err != nil {

View File

@ -24,12 +24,15 @@ import (
type controllerTestSuite struct {
suite.Suite
ctl *controller
proMgr *htesting.FakeProjectManager
repoMgr *htesting.FakeRepositoryManager
}
func (c *controllerTestSuite) SetupTest() {
c.proMgr = &htesting.FakeProjectManager{}
c.repoMgr = &htesting.FakeRepositoryManager{}
c.ctl = &controller{
proMgr: c.proMgr,
repoMgr: c.repoMgr,
}
}
@ -43,7 +46,7 @@ func (c *controllerTestSuite) TestEnsure() {
Name: "library/hello-world",
},
}, nil)
created, id, err := c.ctl.Ensure(nil, 1, "library/hello-world")
created, id, err := c.ctl.Ensure(nil, "library/hello-world")
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.False(created)
@ -54,10 +57,14 @@ func (c *controllerTestSuite) TestEnsure() {
// doesn't exist
c.repoMgr.On("List").Return(0, []*models.RepoRecord{}, nil)
c.proMgr.On("Get").Return(&models.Project{
ProjectID: 1,
}, nil)
c.repoMgr.On("Create").Return(1, nil)
created, id, err = c.ctl.Ensure(nil, 1, "library/hello-world")
created, id, err = c.ctl.Ensure(nil, "library/hello-world")
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.proMgr.AssertExpectations(c.T())
c.True(created)
c.Equal(int64(1), id)
}

View File

@ -16,11 +16,8 @@ package artifact
import (
"context"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact/dao"
"github.com/goharbor/harbor/src/pkg/q"
"strconv"
"strings"
"time"
)
@ -99,24 +96,6 @@ func (m *manager) Create(ctx context.Context, artifact *Artifact) (int64, error)
return id, nil
}
func (m *manager) Delete(ctx context.Context, id int64) error {
// check whether the artifact is referenced by other artifacts
references, err := m.dao.ListReferences(ctx, &q.Query{
Keywords: map[string]interface{}{
"child_id": id,
},
})
if err != nil {
return err
}
if len(references) > 0 {
var ids []string
for _, reference := range references {
ids = append(ids, strconv.FormatInt(reference.ParentID, 10))
}
return ierror.PreconditionFailedError(nil).
WithMessage("artifact %d is referenced by other artifacts: %s", id, strings.Join(ids, ","))
}
// delete references
if err := m.dao.DeleteReferences(ctx, id); err != nil {
return err

View File

@ -16,7 +16,6 @@ package artifact
import (
"context"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact/dao"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/stretchr/testify/mock"
@ -180,28 +179,9 @@ func (m *managerTestSuite) TestCreate() {
}
func (m *managerTestSuite) TestDelete() {
// referenced by other artifacts, delete failed
m.dao.On("ListReferences").Return([]*dao.ArtifactReference{
{
ParentID: 1,
ChildID: 1,
},
}, nil)
err := m.mgr.Delete(nil, 1)
m.Require().NotNil(err)
m.dao.AssertExpectations(m.T())
e, ok := err.(*ierror.Error)
m.Require().True(ok)
m.Equal(ierror.PreconditionCode, e.Code)
// reset the mock
m.SetupTest()
// // referenced by no artifacts
m.dao.On("ListReferences").Return([]*dao.ArtifactReference{}, nil)
m.dao.On("Delete", mock.Anything).Return(nil)
m.dao.On("DeleteReferences").Return(nil)
err = m.mgr.Delete(nil, 1)
err := m.mgr.Delete(nil, 1)
m.Require().Nil(err)
m.dao.AssertExpectations(m.T())
}

View File

@ -22,7 +22,7 @@ import (
// Handle generates the HTTP status code and error payload and writes them to the response
func Handle(w http.ResponseWriter, req *http.Request, err error) {
log.Errorf("failed to handle the request %s: %v", req.URL, err)
log.Errorf("failed to handle the request %s %s: %v", req.Method, req.URL, err)
statusCode, payload := serror.APIError(err)
w.WriteHeader(statusCode)
w.Write([]byte(payload))

View File

@ -15,11 +15,6 @@
package registry
import (
"net/http"
"net/http/httputil"
"net/url"
"github.com/goharbor/harbor/src/pkg/project"
pkg_repo "github.com/goharbor/harbor/src/pkg/repository"
pkg_tag "github.com/goharbor/harbor/src/pkg/tag"
"github.com/goharbor/harbor/src/server/middleware"
@ -32,6 +27,9 @@ import (
"github.com/goharbor/harbor/src/server/registry/manifest"
"github.com/goharbor/harbor/src/server/registry/tag"
"github.com/gorilla/mux"
"net/http"
"net/http/httputil"
"net/url"
)
// New return the registry instance to handle the registry APIs
@ -53,9 +51,9 @@ func New(url *url.URL) http.Handler {
// handle manifest
// TODO maybe we should split it into several sub routers based on the method
manifestRouter := rootRouter.Path("/v2/{name:.*}/manifests/{reference}").Subrouter()
manifestRouter.NewRoute().Methods(http.MethodGet).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), manifestinfo.Middleware(), regtoken.Middleware()))
manifestRouter.NewRoute().Methods(http.MethodHead).Handler(manifest.NewHandler(project.Mgr, proxy))
manifestRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), readonly.Middleware(), manifestinfo.Middleware(), immutable.MiddlewareDelete()))
manifestRouter.NewRoute().Methods(http.MethodGet).Handler(middleware.WithMiddlewares(manifest.NewHandler(proxy), manifestinfo.Middleware(), regtoken.Middleware()))
manifestRouter.NewRoute().Methods(http.MethodHead).Handler(manifest.NewHandler(proxy))
manifestRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(manifest.NewHandler(proxy), readonly.Middleware(), manifestinfo.Middleware(), immutable.MiddlewareDelete()))
// handle blob
// as we need to apply middleware to the blob requests, so create a sub router to handle the blob APIs

View File

@ -17,37 +17,35 @@ package manifest
import (
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/api/repository"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/internal"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/server/registry/error"
"github.com/goharbor/harbor/src/server/router"
"github.com/gorilla/mux"
"github.com/opencontainers/go-digest"
"net/http"
"net/http/httputil"
"strings"
)
// NewHandler returns the handler to handler manifest requests
// TODO the parameters aren't required, use the global variables instead
func NewHandler(proMgr project.Manager, proxy *httputil.ReverseProxy) http.Handler {
func NewHandler(proxy *httputil.ReverseProxy) http.Handler {
return &handler{
proMgr: proMgr,
proxy: proxy,
repoCtl: repository.Ctl,
artCtl: artifact.Ctl,
proxy: proxy,
}
}
type handler struct {
proMgr project.Manager
proxy *httputil.ReverseProxy
repoCtl repository.Controller
artCtl artifact.Controller
proxy *httputil.ReverseProxy
}
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodHead:
h.head(w, req)
case http.MethodGet:
case http.MethodHead, http.MethodGet:
h.get(w, req)
case http.MethodDelete:
h.delete(w, req)
@ -56,42 +54,59 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
// make sure the artifact exist before proxying the request to the backend registry
func (h *handler) head(w http.ResponseWriter, req *http.Request) {
// TODO check the existence
h.proxy.ServeHTTP(w, req)
}
// make sure the artifact exist before proxying the request to the backend registry
func (h *handler) get(w http.ResponseWriter, req *http.Request) {
// TODO check the existence
// check the existence in the database first
vars := mux.Vars(req)
reference := vars["reference"]
artifact, err := h.artCtl.GetByReference(req.Context(), vars["name"], reference, nil)
if err != nil {
error.Handle(w, req, err)
return
}
// the reference is tag, replace it with digest
if _, err = digest.Parse(reference); err != nil {
req = req.Clone(req.Context())
req.URL.Path = strings.TrimSuffix(req.URL.Path, reference) + artifact.Digest
req.URL.RawPath = ""
req.URL.RawPath = req.URL.EscapedPath()
}
h.proxy.ServeHTTP(w, req)
// TODO fire event(only for GET method), add access log in the event handler
}
func (h *handler) delete(w http.ResponseWriter, req *http.Request) {
// TODO implement, just delete from database
// just delete the artifact from database
vars := mux.Vars(req)
artifact, err := h.artCtl.GetByReference(req.Context(), vars["name"], vars["reference"], nil)
if err != nil {
error.Handle(w, req, err)
return
}
if err = h.artCtl.Delete(req.Context(), artifact.ID); err != nil {
error.Handle(w, req, err)
return
}
// TODO fire event, add access log in the event handler
}
func (h *handler) put(w http.ResponseWriter, req *http.Request) {
repositoryName, err := router.Param(req.Context(), ":splat")
repository, err := router.Param(req.Context(), ":splat")
if err != nil {
error.Handle(w, req, err)
return
}
projectName, _ := utils.ParseRepository(repositoryName)
project, err := h.proMgr.Get(projectName)
reference, err := router.Param(req.Context(), ":reference")
if err != nil {
error.Handle(w, req, err)
return
}
if project == nil {
error.Handle(w, req,
ierror.NotFoundError(nil).WithMessage("project %s not found", projectName))
return
}
// make sure the repository exist before pushing the manifest
_, repositoryID, err := repository.Ctl.Ensure(req.Context(), project.ProjectID, repositoryName)
_, repositoryID, err := h.repoCtl.Ensure(req.Context(), repository)
if err != nil {
error.Handle(w, req, err)
return
@ -112,23 +127,14 @@ func (h *handler) put(w http.ResponseWriter, req *http.Request) {
// https://github.com/docker/distribution/issues/2625
var tags []string
var dgt string
reference, err := router.Param(req.Context(), ":reference")
if err != nil {
error.Handle(w, req, err)
return
}
dg, err := digest.Parse(reference)
if err == nil {
// the reference is digest
dgt = dg.String()
} else {
// the reference is tag, get the digest from the response header
dgt := reference
// the reference is tag, get the digest from the response header
if _, err = digest.Parse(reference); err != nil {
dgt = buffer.Header().Get("Docker-Content-Digest")
tags = append(tags, reference)
}
_, _, err = artifact.Ctl.Ensure(req.Context(), repositoryID, dgt, tags...)
_, _, err = h.artCtl.Ensure(req.Context(), repositoryID, dgt, tags...)
if err != nil {
error.Handle(w, req, err)
return

View File

@ -16,7 +16,6 @@ package registry
import (
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/server/middleware/immutable"
"github.com/goharbor/harbor/src/server/middleware/manifestinfo"
"github.com/goharbor/harbor/src/server/middleware/readonly"
@ -41,5 +40,5 @@ func RegisterRoutes() {
Middleware(readonly.Middleware()).
Middleware(manifestinfo.Middleware()).
Middleware(immutable.MiddlewarePush()).
Handler(manifest.NewHandler(project.Mgr, proxy))
Handler(manifest.NewHandler(proxy))
}

View File

@ -0,0 +1,45 @@
// 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 testing
import (
"github.com/goharbor/harbor/src/common/models"
"github.com/stretchr/testify/mock"
)
// FakeProjectManager is a fake project manager that implement src/pkg/project.Manager interface
type FakeProjectManager struct {
mock.Mock
}
// List ...
func (f *FakeProjectManager) List(query ...*models.ProjectQueryParam) ([]*models.Project, error) {
args := f.Called()
var projects []*models.Project
if args.Get(0) != nil {
projects = args.Get(0).([]*models.Project)
}
return projects, args.Error(1)
}
// Get ...
func (f *FakeProjectManager) Get(interface{}) (*models.Project, error) {
args := f.Called()
var project *models.Project
if args.Get(0) != nil {
project = args.Get(0).(*models.Project)
}
return project, args.Error(1)
}