mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-19 15:17:43 +01:00
Merge pull request #10587 from ywk253100/200122_route
Register registry handler with the new methods of Route
This commit is contained in:
commit
fa41168e8e
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{})
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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"},
|
||||
|
@ -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: "",
|
||||
|
@ -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{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -1 +0,0 @@
|
||||
package blob
|
@ -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{
|
@ -1 +0,0 @@
|
||||
package catalog
|
@ -12,6 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package manifest
|
||||
package registry
|
||||
|
||||
// TODO
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
162
src/server/registry/manifest_test.go
Normal file
162
src/server/registry/manifest_test.go
Normal 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{})
|
||||
}
|
47
src/server/registry/proxy.go
Normal file
47
src/server/registry/proxy.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
@ -1 +0,0 @@
|
||||
package tag
|
17
src/server/registry/tag_test.go
Normal file
17
src/server/registry/tag_test.go
Normal 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
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
102
src/testing/api/artifact/controller.go
Normal file
102
src/testing/api/artifact/controller.go
Normal 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)
|
||||
}
|
64
src/testing/api/repository/controller.go
Normal file
64
src/testing/api/repository/controller.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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 {
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user