Register registry handler with the new methods of Route

Register registry handler with the new methods of Route

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2020-01-22 18:28:08 +08:00
parent 9c0d400817
commit ef3af85a5b
38 changed files with 679 additions and 402 deletions

View File

@ -21,8 +21,8 @@ import (
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/artifact"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
repotesting "github.com/goharbor/harbor/src/testing/pkg/repository"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/suite"
"testing"
@ -212,7 +212,7 @@ type abstractorTestSuite struct {
suite.Suite
abstractor Abstractor
fetcher *blob.FakeFetcher
repoMgr *htesting.FakeRepositoryManager
repoMgr *repotesting.FakeManager
}
func (a *abstractorTestSuite) SetupSuite() {
@ -222,7 +222,7 @@ func (a *abstractorTestSuite) SetupSuite() {
func (a *abstractorTestSuite) SetupTest() {
a.fetcher = &blob.FakeFetcher{}
a.repoMgr = &htesting.FakeRepositoryManager{}
a.repoMgr = &repotesting.FakeManager{}
a.abstractor = &abstractor{
repoMgr: a.repoMgr,
blobFetcher: a.fetcher,

View File

@ -21,7 +21,6 @@ import (
"github.com/goharbor/harbor/src/common/utils/registry"
"github.com/goharbor/harbor/src/common/utils/registry/auth"
"github.com/goharbor/harbor/src/core/config"
coreutils "github.com/goharbor/harbor/src/core/utils"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"io/ioutil"
"net/http"
@ -69,7 +68,7 @@ func (f *fetcher) FetchManifest(repository, digest string) (string, []byte, erro
// TODO re-implement it based on OCI registry driver
func (f *fetcher) FetchLayer(repository, digest string) ([]byte, error) {
// TODO read from cache first
client, err := coreutils.NewRepositoryClientForLocal("admin", repository)
client, err := newRepositoryClient(repository)
if err != nil {
return nil, err
}

View File

@ -17,8 +17,8 @@ package chart
import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/artifact"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite"
"testing"
)
@ -26,12 +26,12 @@ import (
type resolverTestSuite struct {
suite.Suite
resolver *resolver
repoMgr *htesting.FakeRepositoryManager
repoMgr *repository.FakeManager
blobFetcher *blob.FakeFetcher
}
func (r *resolverTestSuite) SetupTest() {
r.repoMgr = &htesting.FakeRepositoryManager{}
r.repoMgr = &repository.FakeManager{}
r.blobFetcher = &blob.FakeFetcher{}
r.resolver = &resolver{
repoMgr: r.repoMgr,

View File

@ -16,7 +16,7 @@ package image
import (
"github.com/goharbor/harbor/src/pkg/artifact"
htesting "github.com/goharbor/harbor/src/testing"
arttesting "github.com/goharbor/harbor/src/testing/pkg/artifact"
"github.com/stretchr/testify/suite"
"testing"
)
@ -24,11 +24,11 @@ import (
type indexResolverTestSuite struct {
suite.Suite
resolver *indexResolver
artMgr *htesting.FakeArtifactManager
artMgr *arttesting.FakeManager
}
func (i *indexResolverTestSuite) SetupTest() {
i.artMgr = &htesting.FakeArtifactManager{}
i.artMgr = &arttesting.FakeManager{}
i.resolver = &indexResolver{
artMgr: i.artMgr,
}

View File

@ -17,8 +17,8 @@ package image
import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/artifact"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite"
"testing"
)
@ -26,12 +26,12 @@ import (
type manifestV2ResolverTestSuite struct {
suite.Suite
resolver *manifestV2Resolver
repoMgr *htesting.FakeRepositoryManager
repoMgr *repository.FakeManager
blobFetcher *blob.FakeFetcher
}
func (m *manifestV2ResolverTestSuite) SetupTest() {
m.repoMgr = &htesting.FakeRepositoryManager{}
m.repoMgr = &repository.FakeManager{}
m.blobFetcher = &blob.FakeFetcher{}
m.resolver = &manifestV2Resolver{
repoMgr: m.repoMgr,

View File

@ -21,7 +21,9 @@ import (
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
htesting "github.com/goharbor/harbor/src/testing"
arttesting "github.com/goharbor/harbor/src/testing/pkg/artifact"
repotesting "github.com/goharbor/harbor/src/testing/pkg/repository"
tagtesting "github.com/goharbor/harbor/src/testing/pkg/tag"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"testing"
@ -40,16 +42,16 @@ func (f *fakeAbstractor) Abstract(ctx context.Context, artifact *artifact.Artifa
type controllerTestSuite struct {
suite.Suite
ctl *controller
repoMgr *htesting.FakeRepositoryManager
artMgr *htesting.FakeArtifactManager
tagMgr *htesting.FakeTagManager
repoMgr *repotesting.FakeManager
artMgr *arttesting.FakeManager
tagMgr *tagtesting.FakeManager
abstractor *fakeAbstractor
}
func (c *controllerTestSuite) SetupTest() {
c.repoMgr = &htesting.FakeRepositoryManager{}
c.artMgr = &htesting.FakeArtifactManager{}
c.tagMgr = &htesting.FakeTagManager{}
c.repoMgr = &repotesting.FakeManager{}
c.artMgr = &arttesting.FakeManager{}
c.tagMgr = &tagtesting.FakeManager{}
c.abstractor = &fakeAbstractor{}
c.ctl = &controller{
repoMgr: c.repoMgr,

View File

@ -35,8 +35,12 @@ type Controller interface {
// 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)
// List repositories according to the query
List(ctx context.Context, query *q.Query) (total int64, repositories []*models.RepoRecord, err error)
// Get the repository specified by ID
Get(ctx context.Context, id int64) (repository *models.RepoRecord, err error)
// GetByName gets the repository specified by name
GetByName(ctx context.Context, name string) (repository *models.RepoRecord, err error)
}
// NewController creates an instance of the default repository controller
@ -93,6 +97,14 @@ func (c *controller) Ensure(ctx context.Context, name string) (bool, int64, erro
return true, id, nil
}
func (c *controller) List(ctx context.Context, query *q.Query) (int64, []*models.RepoRecord, error) {
return c.repoMgr.List(ctx, query)
}
func (c *controller) Get(ctx context.Context, id int64) (*models.RepoRecord, error) {
return c.repoMgr.Get(ctx, id)
}
func (c *controller) GetByName(ctx context.Context, name string) (*models.RepoRecord, error) {
return c.repoMgr.GetByName(ctx, name)
}

View File

@ -16,7 +16,8 @@ package repository
import (
"github.com/goharbor/harbor/src/common/models"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/pkg/project"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite"
"testing"
)
@ -24,13 +25,13 @@ import (
type controllerTestSuite struct {
suite.Suite
ctl *controller
proMgr *htesting.FakeProjectManager
repoMgr *htesting.FakeRepositoryManager
proMgr *project.FakeManager
repoMgr *repository.FakeManager
}
func (c *controllerTestSuite) SetupTest() {
c.proMgr = &htesting.FakeProjectManager{}
c.repoMgr = &htesting.FakeRepositoryManager{}
c.proMgr = &project.FakeManager{}
c.repoMgr = &repository.FakeManager{}
c.ctl = &controller{
proMgr: c.proMgr,
repoMgr: c.repoMgr,
@ -69,6 +70,20 @@ func (c *controllerTestSuite) TestEnsure() {
c.Equal(int64(1), id)
}
func (c *controllerTestSuite) TestList() {
c.repoMgr.On("List").Return(1, []*models.RepoRecord{
{
RepositoryID: 1,
},
}, nil)
total, repositories, err := c.ctl.List(nil, nil)
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.Equal(int64(1), total)
c.Require().Len(repositories, 1)
c.Equal(int64(1), repositories[0].RepositoryID)
}
func (c *controllerTestSuite) TestGet() {
c.repoMgr.On("Get").Return(&models.RepoRecord{
RepositoryID: 1,
@ -79,6 +94,16 @@ func (c *controllerTestSuite) TestGet() {
c.Equal(int64(1), repository.RepositoryID)
}
func (c *controllerTestSuite) TestGetByName() {
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
}, nil)
repository, err := c.ctl.GetByName(nil, "library/hello-world")
c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T())
c.Equal(int64(1), repository.RepositoryID)
}
func TestControllerTestSuite(t *testing.T) {
suite.Run(t, &controllerTestSuite{})
}

View File

@ -217,7 +217,11 @@ func SelfRegistration() (bool, error) {
// RegistryURL ...
func RegistryURL() (string, error) {
return cfgMgr.Get(common.RegistryURL).GetString(), nil
url := os.Getenv("REGISTRY_URL")
if len(url) == 0 {
url = "http://registry:5000"
}
return url, nil
}
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers

View File

@ -76,6 +76,7 @@ require (
github.com/stretchr/testify v1.4.0
github.com/theupdateframework/notary v0.6.1
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect

View File

@ -1,14 +1,13 @@
package retention
import (
htesting "github.com/goharbor/harbor/src/testing"
"strings"
"testing"
"github.com/goharbor/harbor/src/pkg/retention/dep"
"github.com/goharbor/harbor/src/pkg/retention/policy"
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite"
"strings"
"testing"
)
type ControllerTestSuite struct {
@ -29,7 +28,7 @@ func TestController(t *testing.T) {
func (s *ControllerTestSuite) TestPolicy() {
projectMgr := &fakeProjectManager{}
repositoryMgr := &htesting.FakeRepositoryManager{}
repositoryMgr := &repository.FakeManager{}
retentionScheduler := &fakeRetentionScheduler{}
retentionLauncher := &fakeLauncher{}
retentionMgr := NewManager()
@ -127,7 +126,7 @@ func (s *ControllerTestSuite) TestPolicy() {
func (s *ControllerTestSuite) TestExecution() {
projectMgr := &fakeProjectManager{}
repositoryMgr := &htesting.FakeRepositoryManager{}
repositoryMgr := &repository.FakeManager{}
retentionScheduler := &fakeRetentionScheduler{}
retentionLauncher := &fakeLauncher{}
retentionMgr := NewManager()

View File

@ -16,9 +16,6 @@ package retention
import (
"fmt"
htesting "github.com/goharbor/harbor/src/testing"
"testing"
"github.com/goharbor/harbor/src/common/job"
"github.com/goharbor/harbor/src/common/models"
_ "github.com/goharbor/harbor/src/pkg/art/selectors/doublestar"
@ -27,9 +24,11 @@ import (
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
"github.com/goharbor/harbor/src/pkg/retention/q"
hjob "github.com/goharbor/harbor/src/testing/job"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"testing"
)
type fakeProjectManager struct {
@ -132,7 +131,7 @@ func (f *fakeRetentionManager) ListHistories(executionID int64, query *q.Query)
type launchTestSuite struct {
suite.Suite
projectMgr project.Manager
repositoryMgr *htesting.FakeRepositoryManager
repositoryMgr *repository.FakeManager
retentionMgr Manager
jobserviceClient job.Client
}
@ -150,7 +149,7 @@ func (l *launchTestSuite) SetupTest() {
projects: []*models.Project{
pro1, pro2,
}}
l.repositoryMgr = &htesting.FakeRepositoryManager{}
l.repositoryMgr = &repository.FakeManager{}
l.retentionMgr = &fakeRetentionManager{}
l.jobserviceClient = &hjob.MockJobClient{
JobUUID: []string{"1"},

View File

@ -15,16 +15,15 @@
package hook
import (
"testing"
"github.com/goharbor/harbor/src/pkg/scheduler"
"github.com/goharbor/harbor/src/pkg/scheduler/model"
htesting "github.com/goharbor/harbor/src/testing"
schedulertesting "github.com/goharbor/harbor/src/testing/pkg/scheduler"
"github.com/stretchr/testify/require"
"testing"
)
var h = &controller{
manager: &htesting.FakeSchedulerManager{},
manager: &schedulertesting.FakeManager{},
}
func TestUpdateStatus(t *testing.T) {
@ -33,7 +32,7 @@ func TestUpdateStatus(t *testing.T) {
require.NotNil(t, err)
// pass
h.manager.(*htesting.FakeSchedulerManager).Schedules = []*model.Schedule{
h.manager.(*schedulertesting.FakeManager).Schedules = []*model.Schedule{
{
ID: 1,
Status: "",

View File

@ -15,13 +15,12 @@
package scheduler
import (
"testing"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/job"
schedulertesting "github.com/goharbor/harbor/src/testing/pkg/scheduler"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"testing"
)
var sch *scheduler
@ -41,7 +40,7 @@ func (s *schedulerTestSuite) SetupTest() {
// recreate the scheduler object
sch = &scheduler{
jobserviceClient: &job.MockJobClient{},
manager: &htesting.FakeSchedulerManager{},
manager: &schedulertesting.FakeManager{},
}
}

View File

@ -1,22 +0,0 @@
package blob
import (
"net/http"
"net/http/httputil"
)
// NewHandler returns the handler to handler catalog request
func NewHandler(proxy *httputil.ReverseProxy) http.Handler {
return &handler{
proxy: proxy,
}
}
type handler struct {
proxy *httputil.ReverseProxy
}
// ServeHTTP ...
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.proxy.ServeHTTP(w, req)
}

View File

@ -1 +0,0 @@
package blob

View File

@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package catalog
package registry
import (
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/api/repository"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/repository"
reg_error "github.com/goharbor/harbor/src/server/registry/error"
"github.com/goharbor/harbor/src/server/registry/util"
"net/http"
@ -26,26 +26,17 @@ import (
"strconv"
)
// NewHandler returns the handler to handler catalog request
func NewHandler(repoMgr repository.Manager) http.Handler {
return &handler{
repoMgr: repoMgr,
func newRepositoryHandler() http.Handler {
return &repositoryHandler{
repoCtl: repository.Ctl,
}
}
type handler struct {
repoMgr repository.Manager
type repositoryHandler struct {
repoCtl repository.Controller
}
// ServeHTTP ...
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodGet:
h.get(w, req)
}
}
func (h *handler) get(w http.ResponseWriter, req *http.Request) {
func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var maxEntries int
var err error
@ -64,13 +55,13 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
var repoNames []string
// get all repositories
// ToDo filter out the untagged repos
total, repoRecords, err := h.repoMgr.List(req.Context(), nil)
total, repoRecords, err := r.repoCtl.List(req.Context(), nil)
if err != nil {
reg_error.Handle(w, req, err)
return
}
if total <= 0 {
h.sendResponse(w, req, repoNames)
r.sendResponse(w, req, repoNames)
return
}
for _, r := range repoRecords {
@ -78,7 +69,7 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
}
sort.Strings(repoNames)
if !withN {
h.sendResponse(w, req, repoNames)
r.sendResponse(w, req, repoNames)
return
}
@ -109,12 +100,12 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Link", urlStr)
}
h.sendResponse(w, req, resRepos)
r.sendResponse(w, req, resRepos)
return
}
// sendResponse ...
func (h *handler) sendResponse(w http.ResponseWriter, req *http.Request, repositoryNames []string) {
func (r *repositoryHandler) sendResponse(w http.ResponseWriter, req *http.Request, repositoryNames []string) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
enc := json.NewEncoder(w)
if err := enc.Encode(catalogAPIResponse{

View File

@ -1 +0,0 @@
package catalog

View File

@ -12,6 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package manifest
package registry
// TODO

View File

@ -1,91 +0,0 @@
// 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 registry
import (
"github.com/goharbor/harbor/src/server/middleware/contenttrust"
"github.com/goharbor/harbor/src/server/middleware/vulnerable"
"github.com/goharbor/harbor/src/core/config"
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"
"github.com/goharbor/harbor/src/server/middleware/immutable"
"github.com/goharbor/harbor/src/server/middleware/manifestinfo"
"github.com/goharbor/harbor/src/server/middleware/readonly"
"github.com/goharbor/harbor/src/server/middleware/regtoken"
"github.com/goharbor/harbor/src/server/registry/blob"
"github.com/goharbor/harbor/src/server/registry/catalog"
"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
func New(url *url.URL) http.Handler {
// TODO customize the reverse proxy to improve the performance?
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.Director = basicAuthDirector(proxy.Director)
// create the root rooter
rootRouter := mux.NewRouter()
rootRouter.StrictSlash(true)
// handle catalog
rootRouter.Path("/v2/_catalog").Methods(http.MethodGet).Handler(catalog.NewHandler(pkg_repo.Mgr))
// handle list tag
rootRouter.Path("/v2/{name:.*}/tags/list").Methods(http.MethodGet).Handler(tag.NewHandler(pkg_repo.Mgr, pkg_tag.Mgr))
// 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(proxy), manifestinfo.Middleware(), regtoken.Middleware(), contenttrust.Middleware(), vulnerable.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
blobRouter := rootRouter.PathPrefix("/v2/{name:.*}/blobs/").Subrouter()
blobRouter.NewRoute().Methods(http.MethodGet).Handler(blob.NewHandler(proxy))
blobRouter.NewRoute().Methods(http.MethodHead).Handler(blob.NewHandler(proxy))
blobRouter.NewRoute().Methods(http.MethodPost).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), readonly.Middleware()))
blobRouter.NewRoute().Methods(http.MethodPut).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), readonly.Middleware()))
blobRouter.NewRoute().Methods(http.MethodPatch).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), readonly.Middleware()))
blobRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(blob.NewHandler(proxy), readonly.Middleware()))
// all other APIs are proxy to the backend docker registry
rootRouter.PathPrefix("/").Handler(proxy)
// register middlewares
// TODO add auth middleware
// TODO apply the existing middlewares
// rootRouter.Use(mux.MiddlewareFunc(middleware))
return rootRouter
}
func basicAuthDirector(d func(*http.Request)) func(*http.Request) {
return func(r *http.Request) {
d(r)
if r != nil && !middleware.SkipInjectRegistryCred(r.Context()) {
u, p := config.RegistryCredential()
r.SetBasicAuth(u, p)
}
}
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package manifest
package registry
import (
"github.com/goharbor/harbor/src/api/artifact"
@ -21,45 +21,16 @@ import (
"github.com/goharbor/harbor/src/internal"
"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
func NewHandler(proxy *httputil.ReverseProxy) http.Handler {
return &handler{
repoCtl: repository.Ctl,
artCtl: artifact.Ctl,
proxy: proxy,
}
}
type handler struct {
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, http.MethodGet:
h.get(w, req)
case http.MethodDelete:
h.delete(w, req)
case http.MethodPut:
h.put(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) {
// 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)
func getManifest(w http.ResponseWriter, req *http.Request) {
repository := router.Param(req.Context(), ":splat")
reference := router.Param(req.Context(), ":reference")
artifact, err := artifact.Ctl.GetByReference(req.Context(), repository, reference, nil)
if err != nil {
error.Handle(w, req, err)
return
@ -69,23 +40,23 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
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)
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) {
// just delete the artifact from database
vars := mux.Vars(req)
artifact, err := h.artCtl.GetByReference(req.Context(), vars["name"], vars["reference"], nil)
// just delete the artifact from database
func deleteManifest(w http.ResponseWriter, req *http.Request) {
repository := router.Param(req.Context(), ":splat")
reference := router.Param(req.Context(), ":reference")
art, err := artifact.Ctl.GetByReference(req.Context(), repository, reference, nil)
if err != nil {
error.Handle(w, req, err)
return
}
if err = h.artCtl.Delete(req.Context(), artifact.ID); err != nil {
if err = artifact.Ctl.Delete(req.Context(), art.ID); err != nil {
error.Handle(w, req, err)
return
}
@ -94,20 +65,12 @@ func (h *handler) delete(w http.ResponseWriter, req *http.Request) {
// TODO fire event, add access log in the event handler
}
func (h *handler) put(w http.ResponseWriter, req *http.Request) {
repository, err := router.Param(req.Context(), ":splat")
if err != nil {
error.Handle(w, req, err)
return
}
reference, err := router.Param(req.Context(), ":reference")
if err != nil {
error.Handle(w, req, err)
return
}
func putManifest(w http.ResponseWriter, req *http.Request) {
repo := router.Param(req.Context(), ":splat")
reference := router.Param(req.Context(), ":reference")
// make sure the repository exist before pushing the manifest
_, repositoryID, err := h.repoCtl.Ensure(req.Context(), repository)
_, repositoryID, err := repository.Ctl.Ensure(req.Context(), repo)
if err != nil {
error.Handle(w, req, err)
return
@ -115,7 +78,7 @@ func (h *handler) put(w http.ResponseWriter, req *http.Request) {
buffer := internal.NewResponseBuffer(w)
// proxy the req to the backend docker registry
h.proxy.ServeHTTP(buffer, req)
proxy.ServeHTTP(buffer, req)
if !buffer.Success() {
if _, err := buffer.Flush(); err != nil {
log.Errorf("failed to flush: %v", err)
@ -135,7 +98,7 @@ func (h *handler) put(w http.ResponseWriter, req *http.Request) {
tags = append(tags, reference)
}
_, _, err = h.artCtl.Ensure(req.Context(), repositoryID, dgt, tags...)
_, _, err = artifact.Ctl.Ensure(req.Context(), repositoryID, dgt, tags...)
if err != nil {
error.Handle(w, req, err)
return

View File

@ -0,0 +1,162 @@
// 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 registry
import (
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/api/repository"
ierror "github.com/goharbor/harbor/src/internal/error"
arttesting "github.com/goharbor/harbor/src/testing/api/artifact"
repotesting "github.com/goharbor/harbor/src/testing/api/repository"
"github.com/stretchr/testify/suite"
"net/http"
"net/http/httptest"
"testing"
)
type manifestTestSuite struct {
suite.Suite
originalRepoCtl repository.Controller
originalArtCtl artifact.Controller
originalProxy http.Handler
repoCtl *repotesting.FakeController
artCtl *arttesting.FakeController
}
func (m *manifestTestSuite) SetupSuite() {
m.originalRepoCtl = repository.Ctl
m.originalArtCtl = artifact.Ctl
m.originalProxy = proxy
}
func (m *manifestTestSuite) SetupTest() {
m.repoCtl = &repotesting.FakeController{}
m.artCtl = &arttesting.FakeController{}
repository.Ctl = m.repoCtl
artifact.Ctl = m.artCtl
}
func (m *manifestTestSuite) TearDownTest() {
proxy = nil
}
func (m *manifestTestSuite) TearDownSuite() {
repository.Ctl = m.originalRepoCtl
artifact.Ctl = m.originalArtCtl
proxy = m.originalProxy
}
func (m *manifestTestSuite) TestGetManifest() {
// doesn't exist
req := httptest.NewRequest(http.MethodGet, "/v2/library/hello-world/manifests/latest", nil)
w := &httptest.ResponseRecorder{}
m.artCtl.On("GetByReference").Return(nil, ierror.New(nil).WithCode(ierror.NotFoundCode))
getManifest(w, req)
m.Equal(http.StatusNotFound, w.Code)
// reset the mock
m.SetupTest()
// exist
proxy = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodGet && req.URL.Path == "/v2/library/hello-world/manifests/sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180" {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusNotFound)
})
// as we cannot set the beego input in the context, here the request doesn't carry reference part
req = httptest.NewRequest(http.MethodGet, "/v2/library/hello-world/manifests/", nil)
w = &httptest.ResponseRecorder{}
art := &artifact.Artifact{}
art.Digest = "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180"
m.artCtl.On("GetByReference").Return(art, nil)
getManifest(w, req)
m.Equal(http.StatusOK, w.Code)
}
func (m *manifestTestSuite) TestDeleteManifest() {
// doesn't exist
req := httptest.NewRequest(http.MethodDelete, "/v2/library/hello-world/manifests/latest", nil)
w := &httptest.ResponseRecorder{}
m.artCtl.On("GetByReference").Return(nil, ierror.New(nil).WithCode(ierror.NotFoundCode))
deleteManifest(w, req)
m.Equal(http.StatusNotFound, w.Code)
// reset the mock
m.SetupTest()
// reset the mock
m.SetupTest()
// exist
proxy = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodPut && req.URL.Path == "/v2/library/hello-world/manifests/latest" {
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("Docker-Content-Digest", "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180")
return
}
w.WriteHeader(http.StatusOK)
})
req = httptest.NewRequest(http.MethodDelete, "/v2/library/hello-world/manifests/latest", nil)
w = &httptest.ResponseRecorder{}
m.artCtl.On("GetByReference").Return(&artifact.Artifact{}, nil)
m.artCtl.On("Delete").Return(nil)
deleteManifest(w, req)
m.Equal(http.StatusAccepted, w.Code)
}
func (m *manifestTestSuite) TestPutManifest() {
// the backend registry response with 500
proxy = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodPut && req.URL.Path == "/v2/library/hello-world/manifests/latest" {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNotFound)
})
req := httptest.NewRequest(http.MethodPut, "/v2/library/hello-world/manifests/latest", nil)
w := &httptest.ResponseRecorder{}
m.repoCtl.On("Ensure").Return(false, 1, nil)
putManifest(w, req)
m.Equal(http.StatusInternalServerError, w.Code)
// reset the mock
m.SetupTest()
// // the backend registry serves the request successfully
proxy = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodPut && req.URL.Path == "/v2/library/hello-world/manifests/latest" {
w.Header().Set("Docker-Content-Digest", "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180")
w.WriteHeader(http.StatusCreated)
return
}
w.WriteHeader(http.StatusNotFound)
})
req = httptest.NewRequest(http.MethodPut, "/v2/library/hello-world/manifests/latest", nil)
w = &httptest.ResponseRecorder{}
m.repoCtl.On("Ensure").Return(false, 1, nil)
m.artCtl.On("Ensure").Return(true, 1, nil)
putManifest(w, req)
m.Equal(http.StatusCreated, w.Code)
}
func TestManifestTestSuite(t *testing.T) {
suite.Run(t, &manifestTestSuite{})
}

View File

@ -0,0 +1,47 @@
// 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 registry
import (
"fmt"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/server/middleware"
"net/http"
"net/http/httputil"
"net/url"
)
var proxy = newProxy()
func newProxy() http.Handler {
regURL, _ := config.RegistryURL()
url, err := url.Parse(regURL)
if err != nil {
panic(fmt.Sprintf("failed to parse the URL of registry: %v", err))
}
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.Director = basicAuthDirector(proxy.Director)
return proxy
}
func basicAuthDirector(d func(*http.Request)) func(*http.Request) {
return func(r *http.Request) {
d(r)
if r != nil && !middleware.SkipInjectRegistryCred(r.Context()) {
u, p := config.RegistryCredential()
r.SetBasicAuth(u, p)
}
}
}

View File

@ -15,39 +15,70 @@
package registry
import (
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/server/middleware/immutable"
"github.com/goharbor/harbor/src/server/middleware/artifactinfo"
"github.com/goharbor/harbor/src/server/middleware/contenttrust"
"github.com/goharbor/harbor/src/server/middleware/immutable"
"github.com/goharbor/harbor/src/server/middleware/manifestinfo"
"github.com/goharbor/harbor/src/server/middleware/readonly"
"github.com/goharbor/harbor/src/server/middleware/regtoken"
"github.com/goharbor/harbor/src/server/middleware/v2auth"
"github.com/goharbor/harbor/src/server/registry/manifest"
"github.com/goharbor/harbor/src/server/middleware/vulnerable"
"github.com/goharbor/harbor/src/server/router"
"net/http"
"net/http/httputil"
"net/url"
)
// RegisterRoutes for OCI registry APIs
func RegisterRoutes() {
// TODO remove
regURL, _ := config.RegistryURL()
url, _ := url.Parse(regURL)
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.Director = basicAuthDirector(proxy.Director)
router.NewRoute().Path("/v2/*").
root := router.NewRoute().
Path("/v2").
Middleware(artifactinfo.Middleware()).
Middleware(v2auth.Middleware()).
Handler(New(url))
router.NewRoute().
Middleware(v2auth.Middleware())
// catalog
root.NewRoute().
Method(http.MethodGet).
Path("/_catalog").
Handler(newRepositoryHandler())
// list tags
root.NewRoute().
Method(http.MethodGet).
Path("/*/tags/list").
Handler(newTagHandler())
// manifest
root.NewRoute().
Method(http.MethodGet).
Path("/*/manifests/:reference").
Middleware(manifestinfo.Middleware()).
Middleware(regtoken.Middleware()).
Middleware(contenttrust.Middleware()).
Middleware(vulnerable.Middleware()).
HandlerFunc(getManifest)
root.NewRoute().
Method(http.MethodHead).
Path("/*/manifests/:reference").
HandlerFunc(getManifest)
root.NewRoute().
Method(http.MethodDelete).
Path("/*/manifests/:reference").
Middleware(readonly.Middleware()).
Middleware(manifestinfo.Middleware()).
Middleware(immutable.MiddlewareDelete()).
HandlerFunc(deleteManifest)
root.NewRoute().
Method(http.MethodPut).
Path("/v2/*/manifests/:reference").
Middleware(artifactinfo.Middleware()).
Middleware(v2auth.Middleware()).
Path("/*/manifests/:reference").
Middleware(readonly.Middleware()).
Middleware(manifestinfo.Middleware()).
Middleware(immutable.MiddlewarePush()).
Handler(manifest.NewHandler(proxy))
HandlerFunc(putManifest)
// blob
root.NewRoute().
Method(http.MethodPost).
Method(http.MethodPut).
Method(http.MethodPatch).
Method(http.MethodDelete).
Path("/{name:.*}/blobs/").
Middleware(readonly.Middleware()).
Handler(proxy)
// others
root.NewRoute().Path("/*").Handler(proxy)
}

View File

@ -12,46 +12,36 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package tag
package registry
import (
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/api/repository"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/repository"
"github.com/goharbor/harbor/src/pkg/tag"
reg_error "github.com/goharbor/harbor/src/server/registry/error"
"github.com/goharbor/harbor/src/server/registry/util"
"github.com/gorilla/mux"
"github.com/goharbor/harbor/src/server/router"
"net/http"
"sort"
"strconv"
)
// NewHandler returns the handler to handle listing tag request
func NewHandler(repoMgr repository.Manager, tagMgr tag.Manager) http.Handler {
return &handler{
repoMgr: repoMgr,
tagMgr: tagMgr,
func newTagHandler() http.Handler {
return &tagHandler{
repoCtl: repository.Ctl,
artCtl: artifact.Ctl,
}
}
type handler struct {
repoMgr repository.Manager
tagMgr tag.Manager
type tagHandler struct {
repoCtl repository.Controller
artCtl artifact.Controller
repositoryName string
}
// ServeHTTP ...
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodGet:
h.get(w, req)
}
}
// get return the list of tags
// Content-Type: application/json
@ -64,7 +54,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// ...
// ]
// }
func (h *handler) get(w http.ResponseWriter, req *http.Request) {
func (t *tagHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var maxEntries int
var err error
@ -80,29 +70,26 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
}
}
var repoID int64
var tagNames []string
vars := mux.Vars(req)
h.repositoryName = vars["name"]
repoID, err = h.getRepoID(req)
t.repositoryName = router.Param(req.Context(), ":splat")
repository, err := t.repoCtl.GetByName(req.Context(), t.repositoryName)
if err != nil {
reg_error.Handle(w, req, err)
return
}
// get tags ...
total, tags, err := h.tagMgr.List(req.Context(), &q.Query{
total, tags, err := t.artCtl.Tags(req.Context(), &q.Query{
Keywords: map[string]interface{}{
"RepositoryID": repoID,
},
})
"RepositoryID": repository.RepositoryID,
}}, nil)
if err != nil {
reg_error.Handle(w, req, err)
return
}
if total == 0 {
h.sendResponse(w, req, tagNames)
t.sendResponse(w, req, tagNames)
return
}
@ -111,7 +98,7 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
}
sort.Strings(tagNames)
if !withN {
h.sendResponse(w, req, tagNames)
t.sendResponse(w, req, tagNames)
return
}
@ -141,33 +128,16 @@ func (h *handler) get(w http.ResponseWriter, req *http.Request) {
}
w.Header().Set("Link", urlStr)
}
h.sendResponse(w, req, resTags)
t.sendResponse(w, req, resTags)
return
}
// getRepoID ...
func (h *handler) getRepoID(req *http.Request) (int64, error) {
total, repoRecord, err := h.repoMgr.List(req.Context(), &q.Query{
Keywords: map[string]interface{}{
"name": h.repositoryName,
},
})
if err != nil {
return 0, err
}
if total <= 0 {
err := ierror.New(nil).WithCode(ierror.NotFoundCode).WithMessage("repositoryNotFound")
return 0, err
}
return repoRecord[0].RepositoryID, nil
}
// sendResponse ...
func (h *handler) sendResponse(w http.ResponseWriter, req *http.Request, tagNames []string) {
func (t *tagHandler) sendResponse(w http.ResponseWriter, req *http.Request, tagNames []string) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
enc := json.NewEncoder(w)
if err := enc.Encode(tagsAPIResponse{
Name: h.repositoryName,
Name: t.repositoryName,
Tags: tagNames,
}); err != nil {
reg_error.Handle(w, req, err)

View File

@ -1 +0,0 @@
package tag

View File

@ -0,0 +1,17 @@
// 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 registry
// TODO

View File

@ -16,11 +16,11 @@ package router
import (
"context"
"errors"
"github.com/astaxie/beego"
beegocontext "github.com/astaxie/beego/context"
"github.com/goharbor/harbor/src/server/middleware"
"net/http"
"path/filepath"
)
type contextKeyInput struct{}
@ -32,11 +32,19 @@ func NewRoute() *Route {
// Route stores the information that matches a request
type Route struct {
parent *Route
methods []string
path string
middlewares []middleware.Middleware
}
// NewRoute returns a sub route based on the current one
func (r *Route) NewRoute() *Route {
return &Route{
parent: r,
}
}
// Method sets the method that the route matches
func (r *Route) Method(method string) *Route {
r.methods = append(r.methods, method)
@ -57,33 +65,49 @@ func (r *Route) Middleware(middleware middleware.Middleware) *Route {
// Handler sets the handler that handles the request
func (r *Route) Handler(handler http.Handler) {
methods := r.methods
if len(methods) == 0 && r.parent != nil {
methods = r.parent.methods
}
path := r.path
if r.parent != nil {
path = filepath.Join(r.parent.path, path)
}
var middlewares []middleware.Middleware
if r.parent != nil {
middlewares = r.parent.middlewares
}
middlewares = append(middlewares, r.middlewares...)
filterFunc := beego.FilterFunc(func(ctx *beegocontext.Context) {
ctx.Request = ctx.Request.WithContext(
context.WithValue(ctx.Request.Context(), contextKeyInput{}, ctx.Input))
// TODO remove the WithMiddlewares?
middleware.WithMiddlewares(handler, r.middlewares...).
middleware.WithMiddlewares(handler, middlewares...).
ServeHTTP(ctx.ResponseWriter, ctx.Request)
})
if len(r.methods) == 0 {
if len(methods) == 0 {
beego.Any(r.path, filterFunc)
return
}
for _, method := range r.methods {
for _, method := range methods {
switch method {
case http.MethodGet:
beego.Get(r.path, filterFunc)
beego.Get(path, filterFunc)
case http.MethodHead:
beego.Head(r.path, filterFunc)
beego.Head(path, filterFunc)
case http.MethodPut:
beego.Put(r.path, filterFunc)
beego.Put(path, filterFunc)
case http.MethodPatch:
beego.Patch(r.path, filterFunc)
beego.Patch(path, filterFunc)
case http.MethodPost:
beego.Post(r.path, filterFunc)
beego.Post(path, filterFunc)
case http.MethodDelete:
beego.Delete(r.path, filterFunc)
beego.Delete(path, filterFunc)
case http.MethodOptions:
beego.Options(r.path, filterFunc)
beego.Options(path, filterFunc)
}
}
}
@ -93,28 +117,14 @@ func (r *Route) HandlerFunc(f http.HandlerFunc) {
r.Handler(f)
}
// GetInput returns the input object from the context
func GetInput(context context.Context) (*beegocontext.BeegoInput, error) {
if context == nil {
return nil, errors.New("context is nil")
// Param returns the beego router param by a given key from the context
func Param(ctx context.Context, key string) string {
if ctx == nil {
return ""
}
input, ok := context.Value(contextKeyInput{}).(*beegocontext.BeegoInput)
input, ok := ctx.Value(contextKeyInput{}).(*beegocontext.BeegoInput)
if !ok {
return nil, errors.New("input not found in the context")
return ""
}
return input, nil
}
// Param returns the router param by a given key from the context
func Param(ctx context.Context, key string) (string, error) {
input, err := GetInput(ctx)
if err != nil {
return "", err
}
return input.Param(key), nil
}
// Middleware registers the global middleware that executed for all requests that match the path
func Middleware(path string, middleware middleware.Middleware) {
// TODO add middleware function to register global middleware after upgrading to the latest version of beego
return input.Param(key)
}

View File

@ -32,6 +32,12 @@ func (r *routerTestSuite) SetupTest() {
r.route = NewRoute()
}
func (r *routerTestSuite) TestNewRoute() {
sub := r.route.Path("/v2").NewRoute()
r.Require().NotNil(sub.parent)
r.Equal("/v2", sub.parent.path)
}
func (r *routerTestSuite) TestMethod() {
r.route.Method(http.MethodGet)
r.Equal(http.MethodGet, r.route.methods[0])
@ -53,27 +59,19 @@ func (r *routerTestSuite) TestMiddleware() {
r.Len(r.route.middlewares, 2)
}
func (r *routerTestSuite) TestGetInput() {
func (r *routerTestSuite) TestParam() {
// nil context
_, err := GetInput(nil)
r.Require().NotNil(err)
value := Param(nil, "key")
r.Empty(value)
// context contains wrong type input
_, err = GetInput(context.WithValue(context.Background(), contextKeyInput{}, &Route{}))
r.Require().NotNil(err)
value = Param(context.WithValue(context.Background(), contextKeyInput{}, &Route{}), "key")
r.Empty(value)
// context contains input
input, err := GetInput(context.WithValue(context.Background(), contextKeyInput{}, &beegocontext.BeegoInput{}))
r.Require().Nil(err)
r.Assert().NotNil(input)
}
func (r *routerTestSuite) TestParam() {
// success
input := &beegocontext.BeegoInput{}
input.SetParam("key", "value")
value, err := Param(context.WithValue(context.Background(), contextKeyInput{}, input), "key")
r.Require().Nil(err)
r.Assert().NotNil(input)
value = Param(context.WithValue(context.Background(), contextKeyInput{}, input), "key")
r.Equal("value", value)
}

View File

@ -0,0 +1,102 @@
// 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 artifact
import (
"context"
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/stretchr/testify/mock"
"time"
)
// FakeController is a fake artifact controller that implement src/api/artifact.Controller interface
type FakeController struct {
mock.Mock
}
// Ensure ...
func (f *FakeController) Ensure(ctx context.Context, repositoryID int64, digest string, tags ...string) (bool, int64, error) {
args := f.Called()
return args.Bool(0), int64(args.Int(1)), args.Error(2)
}
// List ...
func (f *FakeController) List(ctx context.Context, query *q.Query, option *artifact.Option) (int64, []*artifact.Artifact, error) {
args := f.Called()
var artifacts []*artifact.Artifact
if args.Get(1) != nil {
artifacts = args.Get(1).([]*artifact.Artifact)
}
return int64(args.Int(0)), artifacts, args.Error(2)
}
// Get ...
func (f *FakeController) Get(ctx context.Context, id int64, option *artifact.Option) (*artifact.Artifact, error) {
args := f.Called()
var art *artifact.Artifact
if args.Get(0) != nil {
art = args.Get(0).(*artifact.Artifact)
}
return art, args.Error(1)
}
// GetByReference ...
func (f *FakeController) GetByReference(ctx context.Context, repository, reference string, option *artifact.Option) (*artifact.Artifact, error) {
args := f.Called()
var art *artifact.Artifact
if args.Get(0) != nil {
art = args.Get(0).(*artifact.Artifact)
}
return art, args.Error(1)
}
// Delete ...
func (f *FakeController) Delete(ctx context.Context, id int64) (err error) {
args := f.Called()
return args.Error(0)
}
// Tags ...
func (f *FakeController) Tags(ctx context.Context, query *q.Query, option *artifact.TagOption) (int64, []*artifact.Tag, error) {
args := f.Called()
var tags []*artifact.Tag
if args.Get(1) != nil {
tags = args.Get(1).([]*artifact.Tag)
}
return int64(args.Int(0)), tags, args.Error(2)
}
// DeleteTag ...
func (f *FakeController) DeleteTag(ctx context.Context, tagID int64) (err error) {
args := f.Called()
return args.Error(0)
}
// UpdatePullTime ...
func (f *FakeController) UpdatePullTime(ctx context.Context, artifactID int64, tagID int64, time time.Time) error {
args := f.Called()
return args.Error(0)
}
// GetSubResource ...
func (f *FakeController) GetSubResource(ctx context.Context, artifactID int64, resource string) (*artifact.Resource, error) {
args := f.Called()
var res *artifact.Resource
if args.Get(0) != nil {
res = args.Get(0).(*artifact.Resource)
}
return res, args.Error(1)
}

View File

@ -0,0 +1,64 @@
// 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 repository
import (
"context"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/stretchr/testify/mock"
)
// FakeController is a fake repository controller that implement src/api/repository.Controller interface
type FakeController struct {
mock.Mock
}
// Ensure ...
func (f *FakeController) Ensure(ctx context.Context, name string) (bool, int64, error) {
args := f.Called()
return args.Bool(0), int64(args.Int(1)), args.Error(2)
}
// List ...
func (f *FakeController) List(ctx context.Context, query *q.Query) (int64, []*models.RepoRecord, error) {
args := f.Called()
var repositories []*models.RepoRecord
if args.Get(1) != nil {
repositories = args.Get(1).([]*models.RepoRecord)
}
return int64(args.Int(0)), repositories, args.Error(2)
}
// Get ...
func (f *FakeController) Get(ctx context.Context, id int64) (*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)
}
// GetByName ...
func (f *FakeController) GetByName(ctx context.Context, name string) (*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)
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
package artifact
import (
"context"
@ -22,13 +22,13 @@ import (
"time"
)
// FakeArtifactManager is a fake artifact manager that implement src/pkg/artifact.Manager interface
type FakeArtifactManager struct {
// FakeManager is a fake artifact manager that implement src/pkg/artifact.Manager interface
type FakeManager struct {
mock.Mock
}
// List ...
func (f *FakeArtifactManager) List(ctx context.Context, query *q.Query) (int64, []*artifact.Artifact, error) {
func (f *FakeManager) List(ctx context.Context, query *q.Query) (int64, []*artifact.Artifact, error) {
args := f.Called()
var artifacts []*artifact.Artifact
if args.Get(1) != nil {
@ -38,7 +38,7 @@ func (f *FakeArtifactManager) List(ctx context.Context, query *q.Query) (int64,
}
// Get ...
func (f *FakeArtifactManager) Get(ctx context.Context, id int64) (*artifact.Artifact, error) {
func (f *FakeManager) Get(ctx context.Context, id int64) (*artifact.Artifact, error) {
args := f.Called()
var art *artifact.Artifact
if args.Get(0) != nil {
@ -48,19 +48,19 @@ func (f *FakeArtifactManager) Get(ctx context.Context, id int64) (*artifact.Arti
}
// Create ...
func (f *FakeArtifactManager) Create(ctx context.Context, artifact *artifact.Artifact) (int64, error) {
func (f *FakeManager) Create(ctx context.Context, artifact *artifact.Artifact) (int64, error) {
args := f.Called()
return int64(args.Int(0)), args.Error(1)
}
// Delete ...
func (f *FakeArtifactManager) Delete(ctx context.Context, id int64) error {
func (f *FakeManager) Delete(ctx context.Context, id int64) error {
args := f.Called()
return args.Error(0)
}
// UpdatePullTime ...
func (f *FakeArtifactManager) UpdatePullTime(ctx context.Context, artifactID int64, time time.Time) error {
func (f *FakeManager) UpdatePullTime(ctx context.Context, artifactID int64, time time.Time) error {
args := f.Called()
return args.Error(0)
}

View File

@ -12,20 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
package project
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 {
// FakeManager is a fake project manager that implement src/pkg/project.Manager interface
type FakeManager struct {
mock.Mock
}
// List ...
func (f *FakeProjectManager) List(query ...*models.ProjectQueryParam) ([]*models.Project, error) {
func (f *FakeManager) List(query ...*models.ProjectQueryParam) ([]*models.Project, error) {
args := f.Called()
var projects []*models.Project
if args.Get(0) != nil {
@ -35,7 +35,7 @@ func (f *FakeProjectManager) List(query ...*models.ProjectQueryParam) ([]*models
}
// Get ...
func (f *FakeProjectManager) Get(interface{}) (*models.Project, error) {
func (f *FakeManager) Get(interface{}) (*models.Project, error) {
args := f.Called()
var project *models.Project
if args.Get(0) != nil {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
package repository
import (
"context"
@ -21,13 +21,13 @@ import (
"github.com/stretchr/testify/mock"
)
// FakeRepositoryManager is a fake repository manager that implement src/pkg/repository.Manager interface
type FakeRepositoryManager struct {
// FakeManager is a fake repository manager that implement src/pkg/repository.Manager interface
type FakeManager struct {
mock.Mock
}
// List ...
func (f *FakeRepositoryManager) List(ctx context.Context, query *q.Query) (int64, []*models.RepoRecord, error) {
func (f *FakeManager) List(ctx context.Context, query *q.Query) (int64, []*models.RepoRecord, error) {
args := f.Called()
var repositories []*models.RepoRecord
if args.Get(1) != nil {
@ -37,7 +37,7 @@ func (f *FakeRepositoryManager) List(ctx context.Context, query *q.Query) (int64
}
// Get ...
func (f *FakeRepositoryManager) Get(ctx context.Context, id int64) (*models.RepoRecord, error) {
func (f *FakeManager) Get(ctx context.Context, id int64) (*models.RepoRecord, error) {
args := f.Called()
var repository *models.RepoRecord
if args.Get(0) != nil {
@ -47,7 +47,7 @@ func (f *FakeRepositoryManager) Get(ctx context.Context, id int64) (*models.Repo
}
// GetByName ...
func (f *FakeRepositoryManager) GetByName(ctx context.Context, name string) (*models.RepoRecord, error) {
func (f *FakeManager) GetByName(ctx context.Context, name string) (*models.RepoRecord, error) {
args := f.Called()
var repository *models.RepoRecord
if args.Get(0) != nil {
@ -57,19 +57,19 @@ func (f *FakeRepositoryManager) GetByName(ctx context.Context, name string) (*mo
}
// Delete ...
func (f *FakeRepositoryManager) Delete(ctx context.Context, id int64) error {
func (f *FakeManager) Delete(ctx context.Context, id int64) error {
args := f.Called()
return args.Error(0)
}
// Create ...
func (f *FakeRepositoryManager) Create(ctx context.Context, repository *models.RepoRecord) (int64, error) {
func (f *FakeManager) Create(ctx context.Context, repository *models.RepoRecord) (int64, error) {
args := f.Called()
return int64(args.Int(0)), args.Error(1)
}
// Update ...
func (f *FakeRepositoryManager) Update(ctx context.Context, repository *models.RepoRecord, props ...string) error {
func (f *FakeManager) Update(ctx context.Context, repository *models.RepoRecord, props ...string) error {
args := f.Called()
return args.Error(0)
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
package scheduler
import (
"fmt"
@ -20,14 +20,14 @@ import (
"github.com/goharbor/harbor/src/pkg/scheduler/model"
)
// FakeSchedulerManager ...
type FakeSchedulerManager struct {
// FakeManager ...
type FakeManager struct {
idCounter int64
Schedules []*model.Schedule
}
// Create ...
func (f *FakeSchedulerManager) Create(schedule *model.Schedule) (int64, error) {
func (f *FakeManager) Create(schedule *model.Schedule) (int64, error) {
f.idCounter++
id := f.idCounter
schedule.ID = id
@ -36,7 +36,7 @@ func (f *FakeSchedulerManager) Create(schedule *model.Schedule) (int64, error) {
}
// Update ...
func (f *FakeSchedulerManager) Update(schedule *model.Schedule, props ...string) error {
func (f *FakeManager) Update(schedule *model.Schedule, props ...string) error {
for i, sch := range f.Schedules {
if sch.ID == schedule.ID {
f.Schedules[i] = schedule
@ -47,7 +47,7 @@ func (f *FakeSchedulerManager) Update(schedule *model.Schedule, props ...string)
}
// Delete ...
func (f *FakeSchedulerManager) Delete(id int64) error {
func (f *FakeManager) Delete(id int64) error {
length := len(f.Schedules)
for i, sch := range f.Schedules {
if sch.ID == id {
@ -62,7 +62,7 @@ func (f *FakeSchedulerManager) Delete(id int64) error {
}
// Get ...
func (f *FakeSchedulerManager) Get(id int64) (*model.Schedule, error) {
func (f *FakeManager) Get(id int64) (*model.Schedule, error) {
for _, sch := range f.Schedules {
if sch.ID == id {
return sch, nil
@ -72,6 +72,6 @@ func (f *FakeSchedulerManager) Get(id int64) (*model.Schedule, error) {
}
// List ...
func (f *FakeSchedulerManager) List(...*model.ScheduleQuery) ([]*model.Schedule, error) {
func (f *FakeManager) List(...*model.ScheduleQuery) ([]*model.Schedule, error) {
return f.Schedules, nil
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
package tag
import (
"context"
@ -21,13 +21,13 @@ import (
"github.com/stretchr/testify/mock"
)
// FakeTagManager is a fake tag manager that implement the src/pkg/tag.Manager interface
type FakeTagManager struct {
// FakeManager is a fake tag manager that implement the src/pkg/tag.Manager interface
type FakeManager struct {
mock.Mock
}
// List ...
func (f *FakeTagManager) List(ctx context.Context, query *q.Query) (int64, []*tag.Tag, error) {
func (f *FakeManager) List(ctx context.Context, query *q.Query) (int64, []*tag.Tag, error) {
args := f.Called()
var tags []*tag.Tag
if args.Get(1) != nil {
@ -37,7 +37,7 @@ func (f *FakeTagManager) List(ctx context.Context, query *q.Query) (int64, []*ta
}
// Get ...
func (f *FakeTagManager) Get(ctx context.Context, id int64) (*tag.Tag, error) {
func (f *FakeManager) Get(ctx context.Context, id int64) (*tag.Tag, error) {
args := f.Called()
var tg *tag.Tag
if args.Get(0) != nil {
@ -47,19 +47,19 @@ func (f *FakeTagManager) Get(ctx context.Context, id int64) (*tag.Tag, error) {
}
// Create ...
func (f *FakeTagManager) Create(ctx context.Context, tag *tag.Tag) (int64, error) {
func (f *FakeManager) Create(ctx context.Context, tag *tag.Tag) (int64, error) {
args := f.Called()
return int64(args.Int(0)), args.Error(1)
}
// Update ...
func (f *FakeTagManager) Update(ctx context.Context, tag *tag.Tag, props ...string) error {
func (f *FakeManager) Update(ctx context.Context, tag *tag.Tag, props ...string) error {
args := f.Called()
return args.Error(0)
}
// Delete ...
func (f *FakeTagManager) Delete(ctx context.Context, id int64) error {
func (f *FakeManager) Delete(ctx context.Context, id int64) error {
args := f.Called()
return args.Error(0)
}

View File

@ -36,9 +36,8 @@ Test Case - Edit Project Creation
# Harbor API Test ./tests/apitests/python/test_scan_image.py
Test Case - Manage Project Member
Harbor API Test ./tests/apitests/python/test_manage_project_member.py
# TODO uncomment this after enable content trust middleware
# Test Case - Project Level Policy Content Trust
# Harbor API Test ./tests/apitests/python/test_project_level_policy_content_trust.py
Test Case - Project Level Policy Content Trust
Harbor API Test ./tests/apitests/python/test_project_level_policy_content_trust.py
# TODO uncomment this after we move the accesslog away from registry notificaiton
# TODO potentially #10602 may also fix this.
# Test Case - User View Logs

View File

@ -4,7 +4,7 @@ set -x
set -e
export POSTGRESQL_HOST=$1
export REGISTRY_URL=$1:5000
export REGISTRY_URL=http://$1:5000
export CHROME_BIN=chromium-browser
#export DISPLAY=:99.0
#sh -e /etc/init.d/xvfb start