mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 22:57:38 +01:00
Merge pull request #4419 from ywk253100/180314_filter_by_label
Implement filter repository and tags by label API
This commit is contained in:
commit
8aa63b661c
@ -906,6 +906,11 @@ paths:
|
||||
type: string
|
||||
required: false
|
||||
description: Repo name for filtering results.
|
||||
- name: label_id
|
||||
in: query
|
||||
type: integer
|
||||
required: false
|
||||
description: The ID of label used to filter the result.
|
||||
- name: page
|
||||
in: query
|
||||
type: integer
|
||||
@ -1144,6 +1149,11 @@ paths:
|
||||
type: string
|
||||
required: true
|
||||
description: Relevant repository name.
|
||||
- name: label_ids
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: A list of comma separated label IDs.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
|
@ -277,8 +277,9 @@ create table harbor_resource_label (
|
||||
label_id int NOT NULL,
|
||||
# the resource_id is the ID of project when the resource_type is p
|
||||
# the resource_id is the ID of repository when the resource_type is r
|
||||
# the resource_id is the name of image when the resource_type is i
|
||||
resource_id varchar(256) NOT NULL,
|
||||
resource_id int,
|
||||
# the resource_name is the name of image when the resource_type is i
|
||||
resource_name varchar(256),
|
||||
# 'p' for project
|
||||
# 'r' for repository
|
||||
# 'i' for image
|
||||
|
@ -267,9 +267,12 @@ create table harbor_resource_label (
|
||||
/*
|
||||
the resource_id is the ID of project when the resource_type is p
|
||||
the resource_id is the ID of repository when the resource_type is r
|
||||
the resource_id is the name of image when the resource_type is i
|
||||
*/
|
||||
resource_id varchar(256) NOT NULL,
|
||||
resource_id int,
|
||||
/*
|
||||
the resource_name is the name of image when the resource_type is i
|
||||
*/
|
||||
resource_name varchar(256),
|
||||
/*
|
||||
'p' for project
|
||||
'r' for repository
|
||||
|
@ -47,15 +47,6 @@ func GetRepositoryByName(name string) (*models.RepoRecord, error) {
|
||||
return &r, err
|
||||
}
|
||||
|
||||
// GetAllRepositories ...
|
||||
func GetAllRepositories() ([]*models.RepoRecord, error) {
|
||||
o := GetOrmer()
|
||||
var repos []*models.RepoRecord
|
||||
_, err := o.QueryTable("repository").Limit(-1).
|
||||
OrderBy("Name").All(&repos)
|
||||
return repos, err
|
||||
}
|
||||
|
||||
// DeleteRepository ...
|
||||
func DeleteRepository(name string) error {
|
||||
o := GetOrmer()
|
||||
@ -94,18 +85,6 @@ func RepositoryExists(name string) bool {
|
||||
return o.QueryTable("repository").Filter("name", name).Exist()
|
||||
}
|
||||
|
||||
// GetRepositoryByProjectName ...
|
||||
func GetRepositoryByProjectName(name string) ([]*models.RepoRecord, error) {
|
||||
sql := `select * from repository
|
||||
where project_id = (
|
||||
select project_id from project
|
||||
where name = ?
|
||||
)`
|
||||
repos := []*models.RepoRecord{}
|
||||
_, err := GetOrmer().Raw(sql, name).QueryRows(&repos)
|
||||
return repos, err
|
||||
}
|
||||
|
||||
//GetTopRepos returns the most popular repositories whose project ID is
|
||||
// in projectIDs
|
||||
func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) {
|
||||
@ -124,46 +103,78 @@ func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) {
|
||||
}
|
||||
|
||||
// GetTotalOfRepositories ...
|
||||
func GetTotalOfRepositories(name string) (int64, error) {
|
||||
qs := GetOrmer().QueryTable(&models.RepoRecord{})
|
||||
if len(name) != 0 {
|
||||
qs = qs.Filter("Name__contains", name)
|
||||
func GetTotalOfRepositories(query ...*models.RepositoryQuery) (int64, error) {
|
||||
sql, params := repositoryQueryConditions(query...)
|
||||
sql = `select count(*) ` + sql
|
||||
var total int64
|
||||
if err := GetOrmer().Raw(sql, params).QueryRow(&total); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return qs.Count()
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// GetTotalOfRepositoriesByProject ...
|
||||
func GetTotalOfRepositoriesByProject(projectIDs []int64, name string) (int64, error) {
|
||||
if len(projectIDs) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
qs := GetOrmer().QueryTable(&models.RepoRecord{}).
|
||||
Filter("project_id__in", projectIDs)
|
||||
|
||||
if len(name) != 0 {
|
||||
qs = qs.Filter("Name__contains", name)
|
||||
}
|
||||
|
||||
return qs.Count()
|
||||
}
|
||||
|
||||
// GetRepositoriesByProject ...
|
||||
func GetRepositoriesByProject(projectID int64, name string,
|
||||
limit, offset int64) ([]*models.RepoRecord, error) {
|
||||
|
||||
// GetRepositories ...
|
||||
func GetRepositories(query ...*models.RepositoryQuery) ([]*models.RepoRecord, error) {
|
||||
repositories := []*models.RepoRecord{}
|
||||
|
||||
qs := GetOrmer().QueryTable(&models.RepoRecord{}).
|
||||
Filter("ProjectID", projectID)
|
||||
|
||||
if len(name) != 0 {
|
||||
qs = qs.Filter("Name__contains", name)
|
||||
sql, params := repositoryQueryConditions(query...)
|
||||
sql = `select r.repository_id, r.name, r.project_id, r.description, r.pull_count,
|
||||
r.star_count, r.creation_time, r.update_time ` + sql + `order by r.name `
|
||||
if len(query) > 0 && query[0] != nil {
|
||||
page, size := query[0].Page, query[0].Size
|
||||
if size > 0 {
|
||||
sql += `limit ? `
|
||||
params = append(params, size)
|
||||
if page > 0 {
|
||||
sql += `offset ? `
|
||||
params = append(params, size*(page-1))
|
||||
}
|
||||
}
|
||||
}
|
||||
if limit > 0 {
|
||||
qs = qs.Limit(limit).Offset(offset)
|
||||
}
|
||||
_, err := qs.All(&repositories)
|
||||
|
||||
return repositories, err
|
||||
if _, err := GetOrmer().Raw(sql, params).QueryRows(&repositories); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repositories, nil
|
||||
}
|
||||
|
||||
func repositoryQueryConditions(query ...*models.RepositoryQuery) (string, []interface{}) {
|
||||
params := []interface{}{}
|
||||
sql := `from repository r `
|
||||
if len(query) == 0 || query[0] == nil {
|
||||
return sql, params
|
||||
}
|
||||
q := query[0]
|
||||
if len(q.ProjectName) > 0 {
|
||||
sql += `join project p on r.project_id = p.project_id `
|
||||
}
|
||||
|
||||
if q.LabelID > 0 {
|
||||
sql += `join harbor_resource_label rl on r.repository_id = rl.resource_id
|
||||
and rl.resource_type = 'r' `
|
||||
}
|
||||
sql += `where 1=1 `
|
||||
|
||||
if len(q.Name) > 0 {
|
||||
sql += `and r.name = ? `
|
||||
params = append(params, q.Name)
|
||||
}
|
||||
|
||||
if len(q.ProjectIDs) > 0 {
|
||||
sql += fmt.Sprintf(`and r.project_id in ( %s ) `,
|
||||
paramPlaceholder(len(q.ProjectIDs)))
|
||||
params = append(params, q.ProjectIDs)
|
||||
}
|
||||
|
||||
if len(q.ProjectName) > 0 {
|
||||
sql += `and p.name = ? `
|
||||
params = append(params, q.ProjectName)
|
||||
}
|
||||
|
||||
if q.LabelID > 0 {
|
||||
sql += `and rl.label_id = ? `
|
||||
params = append(params, q.LabelID)
|
||||
}
|
||||
|
||||
return sql, params
|
||||
}
|
||||
|
@ -16,10 +16,11 @@ package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
@ -32,61 +33,93 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetRepositoryByProjectName(t *testing.T) {
|
||||
if err := addRepository(repository); err != nil {
|
||||
t.Fatalf("failed to add repository %s: %v", name, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := deleteRepository(name); err != nil {
|
||||
t.Fatalf("failed to delete repository %s: %v", name, err)
|
||||
}
|
||||
}()
|
||||
func TestGetTotalOfRepositories(t *testing.T) {
|
||||
total, err := GetTotalOfRepositories()
|
||||
require.Nil(t, err)
|
||||
|
||||
repositories, err := GetRepositoryByProjectName(project)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get repositories of project %s: %v",
|
||||
project, err)
|
||||
}
|
||||
err = addRepository(repository)
|
||||
require.Nil(t, err)
|
||||
defer deleteRepository(name)
|
||||
|
||||
if len(repositories) == 0 {
|
||||
t.Fatal("unexpected length of repositories: 0, at least 1")
|
||||
}
|
||||
n, err := GetTotalOfRepositories()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, total+1, n)
|
||||
}
|
||||
|
||||
exist := false
|
||||
for _, repo := range repositories {
|
||||
if repo.Name == name {
|
||||
exist = true
|
||||
func TestGetRepositories(t *testing.T) {
|
||||
// no query
|
||||
repositories, err := GetRepositories()
|
||||
require.Nil(t, err)
|
||||
n := len(repositories)
|
||||
|
||||
err = addRepository(repository)
|
||||
require.Nil(t, err)
|
||||
defer deleteRepository(name)
|
||||
|
||||
repositories, err = GetRepositories()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, n+1, len(repositories))
|
||||
|
||||
// query by name
|
||||
repositories, err = GetRepositories(&models.RepositoryQuery{
|
||||
Name: name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(repositories))
|
||||
assert.Equal(t, name, repositories[0].Name)
|
||||
|
||||
// query by project name
|
||||
repositories, err = GetRepositories(&models.RepositoryQuery{
|
||||
ProjectName: project,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
found := false
|
||||
for _, repository := range repositories {
|
||||
if repository.Name == name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("there is no repository whose name is %s", name)
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
|
||||
func TestGetTotalOfRepositories(t *testing.T) {
|
||||
total, err := GetTotalOfRepositories("")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get total of repositoreis: %v", err)
|
||||
}
|
||||
|
||||
if err := addRepository(repository); err != nil {
|
||||
t.Fatalf("failed to add repository %s: %v", name, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := deleteRepository(name); err != nil {
|
||||
t.Fatalf("failed to delete repository %s: %v", name, err)
|
||||
// query by project ID
|
||||
repositories, err = GetRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: []int64{1},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
found = false
|
||||
for _, repository := range repositories {
|
||||
if repository.Name == name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := GetTotalOfRepositories("")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get total of repositoreis: %v", err)
|
||||
}
|
||||
assert.True(t, found)
|
||||
|
||||
if n != total+1 {
|
||||
t.Errorf("unexpected total: %d != %d", n, total+1)
|
||||
}
|
||||
// query by label ID
|
||||
labelID, err := AddLabel(&models.Label{
|
||||
Name: "label_for_test",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer DeleteLabel(labelID)
|
||||
|
||||
r, err := GetRepositoryByName(name)
|
||||
require.Nil(t, err)
|
||||
|
||||
rlID, err := AddResourceLabel(&models.ResourceLabel{
|
||||
LabelID: labelID,
|
||||
ResourceID: r.RepositoryID,
|
||||
ResourceType: common.ResourceTypeRepository,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer DeleteResourceLabel(rlID)
|
||||
|
||||
repositories, err = GetRepositories(&models.RepositoryQuery{
|
||||
LabelID: labelID,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(repositories))
|
||||
assert.Equal(t, name, repositories[0].Name)
|
||||
}
|
||||
|
||||
func TestGetTopRepos(t *testing.T) {
|
||||
@ -149,112 +182,6 @@ func TestGetTopRepos(t *testing.T) {
|
||||
require.Equal(topRepos[0].Name, repository3.Name)
|
||||
}
|
||||
|
||||
func TestGetTotalOfRepositoriesByProject(t *testing.T) {
|
||||
var projectID int64 = 1
|
||||
repoName := "library/total_count"
|
||||
|
||||
total, err := GetTotalOfRepositoriesByProject([]int64{projectID}, repoName)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get total of repositoreis of project %d: %v", projectID, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := addRepository(&models.RepoRecord{
|
||||
Name: repoName,
|
||||
ProjectID: projectID,
|
||||
}); err != nil {
|
||||
t.Errorf("failed to add repository %s: %v", repoName, err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := deleteRepository(repoName); err != nil {
|
||||
t.Errorf("failed to delete repository %s: %v", name, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := GetTotalOfRepositoriesByProject([]int64{projectID}, repoName)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get total of repositoreis of project %d: %v", projectID, err)
|
||||
return
|
||||
}
|
||||
|
||||
if n != total+1 {
|
||||
t.Errorf("unexpected total: %d != %d", n, total+1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRepositoriesByProject(t *testing.T) {
|
||||
var projectID int64 = 1
|
||||
repoName := "library/repository"
|
||||
|
||||
if err := addRepository(&models.RepoRecord{
|
||||
Name: repoName,
|
||||
ProjectID: projectID,
|
||||
}); err != nil {
|
||||
t.Errorf("failed to add repository %s: %v", repoName, err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := deleteRepository(repoName); err != nil {
|
||||
t.Errorf("failed to delete repository %s: %v", name, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
repositories, err := GetRepositoriesByProject(projectID, repoName, 10, 0)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get repositoreis of project %d: %v", projectID, err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Log(repositories)
|
||||
|
||||
for _, repository := range repositories {
|
||||
if repository.Name == repoName {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Errorf("repository %s not found", repoName)
|
||||
}
|
||||
|
||||
func TestGetAllRepositories(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
var repos []*models.RepoRecord
|
||||
repos, err := GetAllRepositories()
|
||||
require.NoError(err)
|
||||
allBefore := len(repos)
|
||||
|
||||
project1 := models.Project{
|
||||
OwnerID: 1,
|
||||
Name: "projectRepo",
|
||||
}
|
||||
var err2 error
|
||||
project1.ProjectID, err2 = AddProject(project1)
|
||||
require.NoError(err2)
|
||||
|
||||
for i := 0; i < 1200; i++ {
|
||||
end := strconv.Itoa(i)
|
||||
repoRecord := models.RepoRecord{
|
||||
Name: "test" + end,
|
||||
ProjectID: project1.ProjectID,
|
||||
}
|
||||
err := AddRepository(repoRecord)
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
repos, err = GetAllRepositories()
|
||||
require.NoError(err)
|
||||
allAfter := len(repos)
|
||||
|
||||
require.Equal(allAfter, allBefore+1200)
|
||||
|
||||
err = clearRepositoryData()
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func addRepository(repository *models.RepoRecord) error {
|
||||
return AddRepository(*repository)
|
||||
}
|
||||
|
@ -29,14 +29,26 @@ func AddResourceLabel(rl *models.ResourceLabel) (int64, error) {
|
||||
return GetOrmer().Insert(rl)
|
||||
}
|
||||
|
||||
// GetResourceLabel specified by ID
|
||||
func GetResourceLabel(rType, rID string, labelID int64) (*models.ResourceLabel, error) {
|
||||
// GetResourceLabel specified by resource ID or name
|
||||
// Get the ResourceLabel by ResourceID if rIDOrName is int
|
||||
// Get the ResourceLabel by ResourceName if rIDOrName is string
|
||||
func GetResourceLabel(rType string, rIDOrName interface{}, labelID int64) (*models.ResourceLabel, error) {
|
||||
rl := &models.ResourceLabel{
|
||||
ResourceType: rType,
|
||||
ResourceID: rID,
|
||||
LabelID: labelID,
|
||||
}
|
||||
if err := GetOrmer().Read(rl, "ResourceType", "ResourceID", "LabelID"); err != nil {
|
||||
|
||||
var err error
|
||||
id, ok := rIDOrName.(int64)
|
||||
if ok {
|
||||
rl.ResourceID = id
|
||||
err = GetOrmer().Read(rl, "ResourceType", "ResourceID", "LabelID")
|
||||
} else {
|
||||
rl.ResourceName = rIDOrName.(string)
|
||||
err = GetOrmer().Read(rl, "ResourceType", "ResourceName", "LabelID")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
@ -47,13 +59,20 @@ func GetResourceLabel(rType, rID string, labelID int64) (*models.ResourceLabel,
|
||||
}
|
||||
|
||||
// GetLabelsOfResource returns the label list of the resource
|
||||
func GetLabelsOfResource(rType, rID string) ([]*models.Label, error) {
|
||||
// Get the labels by ResourceID if rIDOrName is int, or get the labels by ResourceName
|
||||
func GetLabelsOfResource(rType string, rIDOrName interface{}) ([]*models.Label, error) {
|
||||
sql := `select l.id, l.name, l.description, l.color, l.scope, l.project_id, l.creation_time, l.update_time
|
||||
from harbor_resource_label rl
|
||||
join harbor_label l on rl.label_id=l.id
|
||||
where rl.resource_type = ? and rl.resource_id = ?`
|
||||
where rl.resource_type = ? and`
|
||||
if _, ok := rIDOrName.(int64); ok {
|
||||
sql += ` rl.resource_id = ?`
|
||||
} else {
|
||||
sql += ` rl.resource_name = ?`
|
||||
}
|
||||
|
||||
labels := []*models.Label{}
|
||||
_, err := GetOrmer().Raw(sql, rType, rID).QueryRows(&labels)
|
||||
_, err := GetOrmer().Raw(sql, rType, rIDOrName).QueryRows(&labels)
|
||||
return labels, err
|
||||
}
|
||||
|
||||
@ -65,10 +84,39 @@ func DeleteResourceLabel(id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteLabelsOfResource removes all labels of resource specified by rType and rID
|
||||
func DeleteLabelsOfResource(rType, rID string) error {
|
||||
_, err := GetOrmer().QueryTable(&models.ResourceLabel{}).
|
||||
Filter("ResourceType", rType).
|
||||
Filter("ResourceID", rID).Delete()
|
||||
// DeleteLabelsOfResource removes all labels of the resource
|
||||
func DeleteLabelsOfResource(rType string, rIDOrName interface{}) error {
|
||||
qs := GetOrmer().QueryTable(&models.ResourceLabel{}).
|
||||
Filter("ResourceType", rType)
|
||||
if _, ok := rIDOrName.(int64); ok {
|
||||
qs = qs.Filter("ResourceID", rIDOrName)
|
||||
} else {
|
||||
qs = qs.Filter("ResourceName", rIDOrName)
|
||||
}
|
||||
_, err := qs.Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// ListResourceLabels lists ResourceLabel according to the query conditions
|
||||
func ListResourceLabels(query ...*models.ResourceLabelQuery) ([]*models.ResourceLabel, error) {
|
||||
qs := GetOrmer().QueryTable(&models.ResourceLabel{})
|
||||
if len(query) > 0 {
|
||||
q := query[0]
|
||||
if q.LabelID > 0 {
|
||||
qs = qs.Filter("LabelID", q.LabelID)
|
||||
}
|
||||
if len(q.ResourceType) > 0 {
|
||||
qs = qs.Filter("ResourceType", q.ResourceType)
|
||||
}
|
||||
if q.ResourceID > 0 {
|
||||
qs = qs.Filter("ResourceID", q.ResourceID)
|
||||
}
|
||||
if len(q.ResourceName) > 0 {
|
||||
qs = qs.Filter("ResourceName", q.ResourceName)
|
||||
}
|
||||
}
|
||||
|
||||
rls := []*models.ResourceLabel{}
|
||||
_, err := qs.All(&rls)
|
||||
return rls, err
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func TestMethodsOfResourceLabel(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
defer DeleteLabel(labelID)
|
||||
|
||||
resourceID := "1"
|
||||
var resourceID int64 = 1
|
||||
resourceType := common.ResourceTypeRepository
|
||||
|
||||
// add
|
||||
@ -56,6 +56,16 @@ func TestMethodsOfResourceLabel(t *testing.T) {
|
||||
require.Equal(t, 1, len(labels))
|
||||
assert.Equal(t, id, r.ID)
|
||||
|
||||
// list
|
||||
rls, err := ListResourceLabels(&models.ResourceLabelQuery{
|
||||
LabelID: labelID,
|
||||
ResourceType: resourceType,
|
||||
ResourceID: resourceID,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(rls))
|
||||
assert.Equal(t, id, rls[0].ID)
|
||||
|
||||
// delete
|
||||
err = DeleteResourceLabel(id)
|
||||
require.Nil(t, err)
|
||||
|
@ -69,7 +69,8 @@ func (l *Label) Valid(v *validation.Validation) {
|
||||
type ResourceLabel struct {
|
||||
ID int64 `orm:"pk;auto;column(id)"`
|
||||
LabelID int64 `orm:"column(label_id)"`
|
||||
ResourceID string `orm:"column(resource_id)"`
|
||||
ResourceID int64 `orm:"column(resource_id)"`
|
||||
ResourceName string `orm:"column(resource_name)"`
|
||||
ResourceType string `orm:"column(resource_type)"`
|
||||
CreationTime time.Time `orm:"column(creation_time)"`
|
||||
UpdateTime time.Time `orm:"column(update_time)"`
|
||||
@ -79,3 +80,11 @@ type ResourceLabel struct {
|
||||
func (r *ResourceLabel) TableName() string {
|
||||
return "harbor_resource_label"
|
||||
}
|
||||
|
||||
// ResourceLabelQuery : query parameters for the mapping relationships of resource and label
|
||||
type ResourceLabelQuery struct {
|
||||
LabelID int64
|
||||
ResourceID int64
|
||||
ResourceName string
|
||||
ResourceType string
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ type Project struct {
|
||||
OwnerName string `orm:"-" json:"owner_name"`
|
||||
Togglable bool `orm:"-" json:"togglable"`
|
||||
Role int `orm:"-" json:"current_user_role_id"`
|
||||
RepoCount int `orm:"-" json:"repo_count"`
|
||||
RepoCount int64 `orm:"-" json:"repo_count"`
|
||||
Metadata map[string]string `orm:"-" json:"metadata"`
|
||||
}
|
||||
|
||||
|
@ -37,3 +37,12 @@ type RepoRecord struct {
|
||||
func (rp *RepoRecord) TableName() string {
|
||||
return RepoTable
|
||||
}
|
||||
|
||||
// RepositoryQuery : query parameters for repository
|
||||
type RepositoryQuery struct {
|
||||
Name string
|
||||
ProjectIDs []int64
|
||||
ProjectName string
|
||||
LabelID int64
|
||||
Pagination
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package registry
|
||||
|
||||
import (
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
common_models "github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/replication"
|
||||
"github.com/vmware/harbor/src/replication/models"
|
||||
@ -30,7 +31,9 @@ func (ha *HarborAdaptor) GetNamespace(name string) models.Namespace {
|
||||
|
||||
//GetRepositories is used to get all the repositories under the specified namespace
|
||||
func (ha *HarborAdaptor) GetRepositories(namespace string) []models.Repository {
|
||||
repos, err := dao.GetRepositoryByProjectName(namespace)
|
||||
repos, err := dao.GetRepositories(&common_models.RepositoryQuery{
|
||||
ProjectName: namespace,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to get repositories under namespace %s: %v", namespace, err)
|
||||
return nil
|
||||
|
@ -270,7 +270,9 @@ func (p *ProjectAPI) Deletable() {
|
||||
}
|
||||
|
||||
func deletable(projectID int64) (*deletableResp, error) {
|
||||
count, err := dao.GetTotalOfRepositoriesByProject([]int64{projectID}, "")
|
||||
count, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: []int64{projectID},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -395,13 +397,15 @@ func (p *ProjectAPI) populateProperties(project *models.Project) {
|
||||
}
|
||||
}
|
||||
|
||||
repos, err := dao.GetRepositoryByProjectName(project.Name)
|
||||
total, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: []int64{project.ProjectID},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to get repositories of project %s: %v", project.Name, err)
|
||||
log.Errorf("failed to get total of repositories of project %d: %v", project.ProjectID, err)
|
||||
p.CustomAbort(http.StatusInternalServerError, "")
|
||||
}
|
||||
|
||||
project.RepoCount = len(repos)
|
||||
project.RepoCount = total
|
||||
}
|
||||
|
||||
// Put ...
|
||||
|
@ -96,6 +96,12 @@ func (ra *RepositoryAPI) Get() {
|
||||
return
|
||||
}
|
||||
|
||||
labelID, err := ra.GetInt64("label_id", 0)
|
||||
if err != nil {
|
||||
ra.HandleBadRequest(fmt.Sprintf("invalid label_id: %s", ra.GetString("label_id")))
|
||||
return
|
||||
}
|
||||
|
||||
exist, err := ra.ProjectMgr.Exists(projectID)
|
||||
if err != nil {
|
||||
ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %d",
|
||||
@ -117,33 +123,33 @@ func (ra *RepositoryAPI) Get() {
|
||||
return
|
||||
}
|
||||
|
||||
keyword := ra.GetString("q")
|
||||
query := &models.RepositoryQuery{
|
||||
ProjectIDs: []int64{projectID},
|
||||
Name: ra.GetString("q"),
|
||||
LabelID: labelID,
|
||||
}
|
||||
query.Page, query.Size = ra.GetPaginationParams()
|
||||
|
||||
total, err := dao.GetTotalOfRepositoriesByProject(
|
||||
[]int64{projectID}, keyword)
|
||||
total, err := dao.GetTotalOfRepositories(query)
|
||||
if err != nil {
|
||||
ra.HandleInternalServerError(fmt.Sprintf("failed to get total of repositories of project %d: %v",
|
||||
projectID, err))
|
||||
return
|
||||
}
|
||||
|
||||
page, pageSize := ra.GetPaginationParams()
|
||||
|
||||
repositories, err := getRepositories(projectID,
|
||||
keyword, pageSize, pageSize*(page-1))
|
||||
repositories, err := getRepositories(query)
|
||||
if err != nil {
|
||||
ra.HandleInternalServerError(fmt.Sprintf("failed to get repository: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ra.SetPaginationHeader(total, page, pageSize)
|
||||
ra.SetPaginationHeader(total, query.Page, query.Size)
|
||||
ra.Data["json"] = repositories
|
||||
ra.ServeJSON()
|
||||
}
|
||||
|
||||
func getRepositories(projectID int64, keyword string,
|
||||
limit, offset int64) ([]*repoResp, error) {
|
||||
repositories, err := dao.GetRepositoriesByProject(projectID, keyword, limit, offset)
|
||||
func getRepositories(query *models.RepositoryQuery) ([]*repoResp, error) {
|
||||
repositories, err := dao.GetRepositories(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -171,8 +177,7 @@ func assembleRepos(repositories []*models.RepoRecord) ([]*repoResp, error) {
|
||||
}
|
||||
repo.TagsCount = int64(len(tags))
|
||||
|
||||
labels, err := dao.GetLabelsOfResource(common.ResourceTypeRepository,
|
||||
strconv.FormatInt(repository.RepositoryID, 10))
|
||||
labels, err := dao.GetLabelsOfResource(common.ResourceTypeRepository, repository.RepositoryID)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get labels of repository %s: %v", repository.Name, err)
|
||||
} else {
|
||||
@ -385,6 +390,11 @@ func (ra *RepositoryAPI) GetTag() {
|
||||
// GetTags returns tags of a repository
|
||||
func (ra *RepositoryAPI) GetTags() {
|
||||
repoName := ra.GetString(":splat")
|
||||
labelID, err := ra.GetInt64("label_id", 0)
|
||||
if err != nil {
|
||||
ra.HandleBadRequest(fmt.Sprintf("invalid label_id: %s", ra.GetString("label_id")))
|
||||
return
|
||||
}
|
||||
|
||||
projectName, _ := utils.ParseRepository(repoName)
|
||||
exist, err := ra.ProjectMgr.Exists(projectName)
|
||||
@ -420,7 +430,31 @@ func (ra *RepositoryAPI) GetTags() {
|
||||
return
|
||||
}
|
||||
|
||||
ra.Data["json"] = assembleTags(client, repoName, tags, ra.SecurityCtx.GetUsername())
|
||||
// filter tags by label ID
|
||||
if labelID > 0 {
|
||||
rls, err := dao.ListResourceLabels(&models.ResourceLabelQuery{
|
||||
LabelID: labelID,
|
||||
ResourceType: common.ResourceTypeImage,
|
||||
})
|
||||
if err != nil {
|
||||
ra.HandleInternalServerError(fmt.Sprintf("failed to list resource labels: %v", err))
|
||||
return
|
||||
}
|
||||
labeledTags := map[string]struct{}{}
|
||||
for _, rl := range rls {
|
||||
labeledTags[strings.Split(rl.ResourceName, ":")[1]] = struct{}{}
|
||||
}
|
||||
ts := []string{}
|
||||
for _, tag := range tags {
|
||||
if _, ok := labeledTags[tag]; ok {
|
||||
ts = append(ts, tag)
|
||||
}
|
||||
}
|
||||
tags = ts
|
||||
}
|
||||
|
||||
ra.Data["json"] = assembleTags(client, repoName, tags,
|
||||
ra.SecurityCtx.GetUsername())
|
||||
ra.ServeJSON()
|
||||
}
|
||||
|
||||
@ -443,6 +477,15 @@ func assembleTags(client *registry.Repository, repository string,
|
||||
for _, t := range tags {
|
||||
item := &tagResp{}
|
||||
|
||||
// labels
|
||||
image := fmt.Sprintf("%s:%s", repository, t)
|
||||
labels, err := dao.GetLabelsOfResource(common.ResourceTypeImage, image)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get labels of image %s: %v", image, err)
|
||||
} else {
|
||||
item.Labels = labels
|
||||
}
|
||||
|
||||
// the detail information of tag
|
||||
tagDetail, err := getTagDetail(client, t)
|
||||
if err != nil {
|
||||
@ -468,15 +511,6 @@ func assembleTags(client *registry.Repository, repository string,
|
||||
}
|
||||
}
|
||||
|
||||
// labels
|
||||
image := fmt.Sprintf("%s:%s", repository, t)
|
||||
labels, err := dao.GetLabelsOfResource(common.ResourceTypeImage, image)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get labels of image %s: %v", image, err)
|
||||
} else {
|
||||
item.Labels = labels
|
||||
}
|
||||
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
|
@ -144,24 +144,20 @@ func (r *RepositoryLabelAPI) AddToImage() {
|
||||
rl := &models.ResourceLabel{
|
||||
LabelID: r.label.ID,
|
||||
ResourceType: common.ResourceTypeImage,
|
||||
ResourceID: fmt.Sprintf("%s:%s", r.repository.Name, r.tag),
|
||||
ResourceName: fmt.Sprintf("%s:%s", r.repository.Name, r.tag),
|
||||
}
|
||||
r.addLabel(rl)
|
||||
}
|
||||
|
||||
// RemoveFromImage removes the label from an image
|
||||
func (r *RepositoryLabelAPI) RemoveFromImage() {
|
||||
rl := &models.ResourceLabel{
|
||||
LabelID: r.label.ID,
|
||||
ResourceType: common.ResourceTypeImage,
|
||||
ResourceID: fmt.Sprintf("%s:%s", r.repository.Name, r.tag),
|
||||
}
|
||||
r.removeLabel(rl)
|
||||
r.removeLabel(common.ResourceTypeImage,
|
||||
fmt.Sprintf("%s:%s", r.repository.Name, r.tag), r.label.ID)
|
||||
}
|
||||
|
||||
// GetOfRepository returns labels of a repository
|
||||
func (r *RepositoryLabelAPI) GetOfRepository() {
|
||||
r.getLabels(common.ResourceTypeRepository, strconv.FormatInt(r.repository.RepositoryID, 10))
|
||||
r.getLabels(common.ResourceTypeRepository, r.repository.RepositoryID)
|
||||
}
|
||||
|
||||
// AddToRepository adds the label to a repository
|
||||
@ -169,26 +165,21 @@ func (r *RepositoryLabelAPI) AddToRepository() {
|
||||
rl := &models.ResourceLabel{
|
||||
LabelID: r.label.ID,
|
||||
ResourceType: common.ResourceTypeRepository,
|
||||
ResourceID: strconv.FormatInt(r.repository.RepositoryID, 10),
|
||||
ResourceID: r.repository.RepositoryID,
|
||||
}
|
||||
r.addLabel(rl)
|
||||
}
|
||||
|
||||
// RemoveFromRepository removes the label from a repository
|
||||
func (r *RepositoryLabelAPI) RemoveFromRepository() {
|
||||
rl := &models.ResourceLabel{
|
||||
LabelID: r.label.ID,
|
||||
ResourceType: common.ResourceTypeRepository,
|
||||
ResourceID: strconv.FormatInt(r.repository.RepositoryID, 10),
|
||||
}
|
||||
r.removeLabel(rl)
|
||||
r.removeLabel(common.ResourceTypeRepository, r.repository.RepositoryID, r.label.ID)
|
||||
}
|
||||
|
||||
func (r *RepositoryLabelAPI) getLabels(rType, rID string) {
|
||||
labels, err := dao.GetLabelsOfResource(rType, rID)
|
||||
func (r *RepositoryLabelAPI) getLabels(rType string, rIDOrName interface{}) {
|
||||
labels, err := dao.GetLabelsOfResource(rType, rIDOrName)
|
||||
if err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to get labels of resource %s %s: %v",
|
||||
rType, rID, err))
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to get labels of resource %s %v: %v",
|
||||
rType, rIDOrName, err))
|
||||
return
|
||||
}
|
||||
r.Data["json"] = labels
|
||||
@ -196,10 +187,16 @@ func (r *RepositoryLabelAPI) getLabels(rType, rID string) {
|
||||
}
|
||||
|
||||
func (r *RepositoryLabelAPI) addLabel(rl *models.ResourceLabel) {
|
||||
rlabel, err := dao.GetResourceLabel(rl.ResourceType, rl.ResourceID, rl.LabelID)
|
||||
var rIDOrName interface{}
|
||||
if rl.ResourceID != 0 {
|
||||
rIDOrName = rl.ResourceID
|
||||
} else {
|
||||
rIDOrName = rl.ResourceName
|
||||
}
|
||||
rlabel, err := dao.GetResourceLabel(rl.ResourceType, rIDOrName, rl.LabelID)
|
||||
if err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %s: %v",
|
||||
rl.LabelID, rl.ResourceType, rl.ResourceID, err))
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %v: %v",
|
||||
rl.LabelID, rl.ResourceType, rIDOrName, err))
|
||||
return
|
||||
}
|
||||
|
||||
@ -208,8 +205,8 @@ func (r *RepositoryLabelAPI) addLabel(rl *models.ResourceLabel) {
|
||||
return
|
||||
}
|
||||
if _, err := dao.AddResourceLabel(rl); err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to add label %d to resource %s %s: %v",
|
||||
rl.LabelID, rl.ResourceType, rl.ResourceID, err))
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to add label %d to resource %s %v: %v",
|
||||
rl.LabelID, rl.ResourceType, rIDOrName, err))
|
||||
return
|
||||
}
|
||||
|
||||
@ -217,22 +214,22 @@ func (r *RepositoryLabelAPI) addLabel(rl *models.ResourceLabel) {
|
||||
r.Redirect(http.StatusOK, strconv.FormatInt(rl.LabelID, 10))
|
||||
}
|
||||
|
||||
func (r *RepositoryLabelAPI) removeLabel(rl *models.ResourceLabel) {
|
||||
rlabel, err := dao.GetResourceLabel(rl.ResourceType, rl.ResourceID, rl.LabelID)
|
||||
func (r *RepositoryLabelAPI) removeLabel(rType string, rIDOrName interface{}, labelID int64) {
|
||||
rl, err := dao.GetResourceLabel(rType, rIDOrName, labelID)
|
||||
if err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %s: %v",
|
||||
rl.LabelID, rl.ResourceType, rl.ResourceID, err))
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %v: %v",
|
||||
labelID, rType, rIDOrName, err))
|
||||
return
|
||||
}
|
||||
|
||||
if rlabel == nil {
|
||||
if rl == nil {
|
||||
r.HandleNotFound(fmt.Sprintf("label %d of resource %s %s not found",
|
||||
rl.LabelID, rl.ResourceType, rl.ResourceID))
|
||||
labelID, rType, rIDOrName))
|
||||
return
|
||||
}
|
||||
if err = dao.DeleteResourceLabel(rlabel.ID); err != nil {
|
||||
if err = dao.DeleteResourceLabel(rl.ID); err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to delete resource label record %d: %v",
|
||||
rlabel.ID, err))
|
||||
rl.ID, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func TestGetRepos(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
apiTest := newHarborAPI()
|
||||
projectID := "1"
|
||||
keyword := "hello-world"
|
||||
keyword := "library/hello-world"
|
||||
|
||||
fmt.Println("Testing Repos Get API")
|
||||
//-------------------case 1 : response code = 200------------------------//
|
||||
|
@ -99,13 +99,15 @@ func (s *SearchAPI) Get() {
|
||||
}
|
||||
}
|
||||
|
||||
repos, err := dao.GetRepositoryByProjectName(p.Name)
|
||||
total, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: []int64{p.ProjectID},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to get repositories of project %s: %v", p.Name, err)
|
||||
log.Errorf("failed to get total of repositories of project %d: %v", p.ProjectID, err)
|
||||
s.CustomAbort(http.StatusInternalServerError, "")
|
||||
}
|
||||
|
||||
p.RepoCount = len(repos)
|
||||
p.RepoCount = total
|
||||
|
||||
projectResult = append(projectResult, p)
|
||||
}
|
||||
@ -124,7 +126,7 @@ func (s *SearchAPI) Get() {
|
||||
func filterRepositories(projects []*models.Project, keyword string) (
|
||||
[]map[string]interface{}, error) {
|
||||
|
||||
repositories, err := dao.GetAllRepositories()
|
||||
repositories, err := dao.GetRepositories()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -64,17 +64,22 @@ func (s *StatisticAPI) Get() {
|
||||
}
|
||||
|
||||
statistic[PubPC] = (int64)(len(pubProjs))
|
||||
|
||||
ids := []int64{}
|
||||
for _, p := range pubProjs {
|
||||
ids = append(ids, p.ProjectID)
|
||||
if len(pubProjs) == 0 {
|
||||
statistic[PubRC] = 0
|
||||
} else {
|
||||
ids := []int64{}
|
||||
for _, p := range pubProjs {
|
||||
ids = append(ids, p.ProjectID)
|
||||
}
|
||||
n, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: ids,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to get total of public repositories: %v", err)
|
||||
s.CustomAbort(http.StatusInternalServerError, "")
|
||||
}
|
||||
statistic[PubRC] = n
|
||||
}
|
||||
n, err := dao.GetTotalOfRepositoriesByProject(ids, "")
|
||||
if err != nil {
|
||||
log.Errorf("failed to get total of public repositories: %v", err)
|
||||
s.CustomAbort(http.StatusInternalServerError, "")
|
||||
}
|
||||
statistic[PubRC] = n
|
||||
|
||||
if s.SecurityCtx.IsSysAdmin() {
|
||||
result, err := s.ProjectMgr.List(nil)
|
||||
@ -85,7 +90,7 @@ func (s *StatisticAPI) Get() {
|
||||
statistic[TPC] = result.Total
|
||||
statistic[PriPC] = result.Total - statistic[PubPC]
|
||||
|
||||
n, err := dao.GetTotalOfRepositories("")
|
||||
n, err := dao.GetTotalOfRepositories()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get total of repositories: %v", err)
|
||||
s.CustomAbort(http.StatusInternalServerError, "")
|
||||
@ -107,20 +112,25 @@ func (s *StatisticAPI) Get() {
|
||||
}
|
||||
|
||||
statistic[PriPC] = result.Total
|
||||
if result.Total == 0 {
|
||||
statistic[PriRC] = 0
|
||||
} else {
|
||||
ids := []int64{}
|
||||
for _, p := range result.Projects {
|
||||
ids = append(ids, p.ProjectID)
|
||||
}
|
||||
|
||||
ids := []int64{}
|
||||
for _, p := range result.Projects {
|
||||
ids = append(ids, p.ProjectID)
|
||||
n, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: ids,
|
||||
})
|
||||
if err != nil {
|
||||
s.HandleInternalServerError(fmt.Sprintf(
|
||||
"failed to get total of repositories for user %s: %v",
|
||||
s.username, err))
|
||||
return
|
||||
}
|
||||
statistic[PriRC] = n
|
||||
}
|
||||
|
||||
n, err = dao.GetTotalOfRepositoriesByProject(ids, "")
|
||||
if err != nil {
|
||||
s.HandleInternalServerError(fmt.Sprintf(
|
||||
"failed to get total of repositories for user %s: %v",
|
||||
s.username, err))
|
||||
return
|
||||
}
|
||||
statistic[PriRC] = n
|
||||
}
|
||||
|
||||
s.Data["json"] = statistic
|
||||
|
@ -86,7 +86,7 @@ func SyncRegistry(pm promgr.ProjectManager) error {
|
||||
}
|
||||
|
||||
var repoRecordsInDB []*models.RepoRecord
|
||||
repoRecordsInDB, err = dao.GetAllRepositories()
|
||||
repoRecordsInDB, err = dao.GetRepositories()
|
||||
if err != nil {
|
||||
log.Errorf("error occurred while getting all registories. %v", err)
|
||||
return err
|
||||
|
@ -33,7 +33,7 @@ import (
|
||||
|
||||
// ScanAllImages scans all images of Harbor by submiting jobs to jobservice, the whole process will move on if failed to submit any job of a single image.
|
||||
func ScanAllImages() error {
|
||||
repos, err := dao.GetAllRepositories()
|
||||
repos, err := dao.GetRepositories()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list all repositories, error: %v", err)
|
||||
return err
|
||||
@ -46,7 +46,9 @@ func ScanAllImages() error {
|
||||
|
||||
// ScanImagesByProjectID scans all images under a projet, the whole process will move on if failed to submit any job of a single image.
|
||||
func ScanImagesByProjectID(id int64) error {
|
||||
repos, err := dao.GetRepositoriesByProject(id, "", 0, 0)
|
||||
repos, err := dao.GetRepositories(&models.RepositoryQuery{
|
||||
ProjectIDs: []int64{id},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Failed list repositories in project %d, error: %v", id, err)
|
||||
return err
|
||||
|
Loading…
Reference in New Issue
Block a user