mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 14:47:38 +01:00
fix catalog api issue (#11666)
The v2 catalog API needs to filter out the empty repository and the repository which artifacts are all with no tags. 1,In v2.0.0, Harbor does not delete repository even there is no artifact, it's different with v1.10.0 2, Compares with docker distribution, it doesn't return the respository with untagged images. Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
b747fdb68d
commit
ff2a7e61c9
@ -15,10 +15,13 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"github.com/goharbor/harbor/src/controller/repository"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
serror "github.com/goharbor/harbor/src/server/error"
|
||||
"github.com/goharbor/harbor/src/server/registry/util"
|
||||
"net/http"
|
||||
@ -29,11 +32,13 @@ import (
|
||||
func newRepositoryHandler() http.Handler {
|
||||
return &repositoryHandler{
|
||||
repoCtl: repository.Ctl,
|
||||
artCtl: artifact.Ctl,
|
||||
}
|
||||
}
|
||||
|
||||
type repositoryHandler struct {
|
||||
repoCtl repository.Controller
|
||||
artCtl artifact.Controller
|
||||
}
|
||||
|
||||
func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
@ -54,7 +59,6 @@ func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
|
||||
repoNames := make([]string, 0)
|
||||
// get all repositories
|
||||
// ToDo filter out the untagged repos
|
||||
repoRecords, err := r.repoCtl.List(req.Context(), nil)
|
||||
if err != nil {
|
||||
serror.SendError(w, err)
|
||||
@ -64,8 +68,15 @@ func (r *repositoryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
r.sendResponse(w, req, repoNames)
|
||||
return
|
||||
}
|
||||
for _, r := range repoRecords {
|
||||
repoNames = append(repoNames, r.Name)
|
||||
for _, repo := range repoRecords {
|
||||
valid, err := r.validateRepo(req.Context(), repo.RepositoryID)
|
||||
if err != nil {
|
||||
serror.SendError(w, err)
|
||||
return
|
||||
}
|
||||
if valid {
|
||||
repoNames = append(repoNames, repo.Name)
|
||||
}
|
||||
}
|
||||
sort.Strings(repoNames)
|
||||
if !withN {
|
||||
@ -129,6 +140,34 @@ func (r *repositoryHandler) sendResponse(w http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
}
|
||||
|
||||
// empty repo and all of artifacts are untagged should be filtered out.
|
||||
func (r *repositoryHandler) validateRepo(ctx context.Context, repositoryID int64) (bool, error) {
|
||||
arts, err := r.artCtl.List(ctx, &q.Query{
|
||||
Keywords: map[string]interface{}{
|
||||
"RepositoryID": repositoryID,
|
||||
},
|
||||
}, &artifact.Option{
|
||||
WithTag: true,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// empty repo
|
||||
if len(arts) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, art := range arts {
|
||||
if len(art.Tags) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// if all of artifact are untagged, filter out
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type catalogAPIResponse struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
@ -17,8 +17,14 @@ package registry
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"github.com/goharbor/harbor/src/controller/repository"
|
||||
"github.com/goharbor/harbor/src/controller/tag"
|
||||
pkg_art "github.com/goharbor/harbor/src/pkg/artifact"
|
||||
model_tag "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
artifacttesting "github.com/goharbor/harbor/src/testing/controller/artifact"
|
||||
repotesting "github.com/goharbor/harbor/src/testing/controller/repository"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -28,16 +34,21 @@ import (
|
||||
type catalogTestSuite struct {
|
||||
suite.Suite
|
||||
originalRepoCtl repository.Controller
|
||||
originalArtCtl artifact.Controller
|
||||
repoCtl *repotesting.FakeController
|
||||
artCtl *artifacttesting.Controller
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) SetupSuite() {
|
||||
c.originalRepoCtl = repository.Ctl
|
||||
c.originalArtCtl = artifact.Ctl
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) SetupTest() {
|
||||
c.repoCtl = &repotesting.FakeController{}
|
||||
repository.Ctl = c.repoCtl
|
||||
c.artCtl = &artifacttesting.Controller{}
|
||||
artifact.Ctl = c.artCtl
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TearDownTest() {
|
||||
@ -45,6 +56,7 @@ func (c *catalogTestSuite) TearDownTest() {
|
||||
|
||||
func (c *catalogTestSuite) TearDownSuite() {
|
||||
repository.Ctl = c.originalRepoCtl
|
||||
artifact.Ctl = c.originalArtCtl
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalog() {
|
||||
@ -61,6 +73,22 @@ func (c *catalogTestSuite) TestCatalog() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -87,6 +115,22 @@ func (c *catalogTestSuite) TestCatalogPaginationN1() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -114,6 +158,22 @@ func (c *catalogTestSuite) TestCatalogPaginationN2() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -141,6 +201,22 @@ func (c *catalogTestSuite) TestCatalogPaginationN3() {
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
Tags: []*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
@ -154,6 +230,71 @@ func (c *catalogTestSuite) TestCatalogPaginationN3() {
|
||||
c.Equal("hello-world", ctlg.Repositories[0])
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogUntaggedArtifact() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
},
|
||||
{
|
||||
RepositoryID: 2,
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
// untagged artifact
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{
|
||||
Artifact: pkg_art.Artifact{
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
var ctlg struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
decoder := json.NewDecoder(w.Body)
|
||||
err := decoder.Decode(&ctlg)
|
||||
c.Nil(err)
|
||||
c.Equal(0, len(ctlg.Repositories))
|
||||
}
|
||||
|
||||
func (c *catalogTestSuite) TestCatalogEmptyRepo() {
|
||||
c.SetupTest()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v2/_catalog", nil)
|
||||
var w *httptest.ResponseRecorder
|
||||
c.repoCtl.On("List").Return([]*models.RepoRecord{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "hello-world",
|
||||
},
|
||||
{
|
||||
RepositoryID: 2,
|
||||
Name: "busybox",
|
||||
},
|
||||
}, nil)
|
||||
// empty repository
|
||||
mock.OnAnything(c.artCtl, "List").Return([]*artifact.Artifact{
|
||||
{},
|
||||
}, nil)
|
||||
w = httptest.NewRecorder()
|
||||
newRepositoryHandler().ServeHTTP(w, req)
|
||||
c.Equal(http.StatusOK, w.Code)
|
||||
var ctlg struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
decoder := json.NewDecoder(w.Body)
|
||||
err := decoder.Decode(&ctlg)
|
||||
c.Nil(err)
|
||||
c.Equal(0, len(ctlg.Repositories))
|
||||
}
|
||||
|
||||
func TestCatalogTestSuite(t *testing.T) {
|
||||
suite.Run(t, &catalogTestSuite{})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user