mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
Add label filter to filter chain
Label filters are used to filter the resources according to the labels, adding the filters to the chain to enable the function.
This commit is contained in:
parent
92782abce9
commit
c1ebf0ba1e
@ -7,6 +7,8 @@ const (
|
|||||||
FilterItemKindRepository = "repository"
|
FilterItemKindRepository = "repository"
|
||||||
//FilterItemKindTag : Kind of filter item is 'tag'
|
//FilterItemKindTag : Kind of filter item is 'tag'
|
||||||
FilterItemKindTag = "tag"
|
FilterItemKindTag = "tag"
|
||||||
|
//FilterItemKindLabel : Kind of filter item is 'label'
|
||||||
|
FilterItemKindLabel = "label"
|
||||||
|
|
||||||
//AdaptorKindHarbor : Kind of adaptor of Harbor
|
//AdaptorKindHarbor : Kind of adaptor of Harbor
|
||||||
AdaptorKindHarbor = "Harbor"
|
AdaptorKindHarbor = "Harbor"
|
||||||
|
@ -259,17 +259,34 @@ func getCandidates(policy *models.ReplicationPolicy, sourcer *source.Sourcer,
|
|||||||
func buildFilterChain(policy *models.ReplicationPolicy, sourcer *source.Sourcer) source.FilterChain {
|
func buildFilterChain(policy *models.ReplicationPolicy, sourcer *source.Sourcer) source.FilterChain {
|
||||||
filters := []source.Filter{}
|
filters := []source.Filter{}
|
||||||
|
|
||||||
patterns := map[string]string{}
|
fm := map[string][]models.Filter{}
|
||||||
for _, f := range policy.Filters {
|
for _, filter := range policy.Filters {
|
||||||
patterns[f.Kind] = f.Pattern
|
fm[filter.Kind] = append(fm[filter.Kind], filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
registry := sourcer.GetAdaptor(replication.AdaptorKindHarbor)
|
registry := sourcer.GetAdaptor(replication.AdaptorKindHarbor)
|
||||||
// only support repository and tag filter for now
|
// repository filter
|
||||||
|
pattern := ""
|
||||||
|
repoFilters := fm[replication.FilterItemKindRepository]
|
||||||
|
if len(repoFilters) > 0 {
|
||||||
|
pattern = repoFilters[0].Value.(string)
|
||||||
|
}
|
||||||
filters = append(filters,
|
filters = append(filters,
|
||||||
source.NewRepositoryFilter(patterns[replication.FilterItemKindRepository], registry))
|
source.NewRepositoryFilter(pattern, registry))
|
||||||
|
// tag filter
|
||||||
|
pattern = ""
|
||||||
|
tagFilters := fm[replication.FilterItemKindTag]
|
||||||
|
if len(tagFilters) > 0 {
|
||||||
|
pattern = tagFilters[0].Value.(string)
|
||||||
|
}
|
||||||
filters = append(filters,
|
filters = append(filters,
|
||||||
source.NewTagFilter(patterns[replication.FilterItemKindTag], registry))
|
source.NewTagFilter(pattern, registry))
|
||||||
|
// label filters
|
||||||
|
var labelID int64
|
||||||
|
for _, labelFilter := range fm[replication.FilterItemKindLabel] {
|
||||||
|
labelID = labelFilter.Value.(int64)
|
||||||
|
filters = append(filters, source.NewLabelFilter(labelID))
|
||||||
|
}
|
||||||
|
|
||||||
return source.NewDefaultFilterChain(filters)
|
return source.NewDefaultFilterChain(filters)
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func TestGetCandidates(t *testing.T) {
|
|||||||
Filters: []models.Filter{
|
Filters: []models.Filter{
|
||||||
models.Filter{
|
models.Filter{
|
||||||
Kind: replication.FilterItemKindTag,
|
Kind: replication.FilterItemKindTag,
|
||||||
Pattern: "*",
|
Value: "*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Trigger: &models.Trigger{
|
Trigger: &models.Trigger{
|
||||||
@ -112,11 +112,22 @@ func TestGetCandidates(t *testing.T) {
|
|||||||
policy.Filters = []models.Filter{
|
policy.Filters = []models.Filter{
|
||||||
models.Filter{
|
models.Filter{
|
||||||
Kind: replication.FilterItemKindTag,
|
Kind: replication.FilterItemKindTag,
|
||||||
Pattern: "release-*",
|
Value: "release-*",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result = getCandidates(policy, sourcer, metadata)
|
result = getCandidates(policy, sourcer, metadata)
|
||||||
assert.Equal(t, 1, len(result))
|
assert.Equal(t, 1, len(result))
|
||||||
|
|
||||||
|
// test label filter
|
||||||
|
test.InitDatabaseFromEnv()
|
||||||
|
policy.Filters = []models.Filter{
|
||||||
|
models.Filter{
|
||||||
|
Kind: replication.FilterItemKindLabel,
|
||||||
|
Value: int64(1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result = getCandidates(policy, sourcer, metadata)
|
||||||
|
assert.Equal(t, 0, len(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildFilterChain(t *testing.T) {
|
func TestBuildFilterChain(t *testing.T) {
|
||||||
@ -125,11 +136,17 @@ func TestBuildFilterChain(t *testing.T) {
|
|||||||
Filters: []models.Filter{
|
Filters: []models.Filter{
|
||||||
models.Filter{
|
models.Filter{
|
||||||
Kind: replication.FilterItemKindRepository,
|
Kind: replication.FilterItemKindRepository,
|
||||||
Pattern: "*",
|
Value: "*",
|
||||||
},
|
},
|
||||||
|
|
||||||
models.Filter{
|
models.Filter{
|
||||||
Kind: replication.FilterItemKindTag,
|
Kind: replication.FilterItemKindTag,
|
||||||
Pattern: "*",
|
Value: "*",
|
||||||
|
},
|
||||||
|
|
||||||
|
models.Filter{
|
||||||
|
Kind: replication.FilterItemKindLabel,
|
||||||
|
Value: int64(1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -137,5 +154,5 @@ func TestBuildFilterChain(t *testing.T) {
|
|||||||
sourcer := source.NewSourcer()
|
sourcer := source.NewSourcer()
|
||||||
|
|
||||||
chain := buildFilterChain(policy, sourcer)
|
chain := buildFilterChain(policy, sourcer)
|
||||||
assert.Equal(t, 2, len(chain.Filters()))
|
assert.Equal(t, 3, len(chain.Filters()))
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@ import (
|
|||||||
// Filter is the data model represents the filter defined by user.
|
// Filter is the data model represents the filter defined by user.
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Pattern string `json:"pattern"`
|
Pattern string `json:"pattern"` // deprecated, use Value instead
|
||||||
|
Value interface{} `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid ...
|
// Valid ...
|
||||||
|
@ -106,6 +106,11 @@ func convertFromPersistModel(policy *persist_models.RepPolicy) (models.Replicati
|
|||||||
if err := json.Unmarshal([]byte(policy.Filters), &filters); err != nil {
|
if err := json.Unmarshal([]byte(policy.Filters), &filters); err != nil {
|
||||||
return models.ReplicationPolicy{}, err
|
return models.ReplicationPolicy{}, err
|
||||||
}
|
}
|
||||||
|
for i := range filters {
|
||||||
|
if filters[i].Value == nil && len(filters[i].Pattern) > 0 {
|
||||||
|
filters[i].Value = filters[i].Pattern
|
||||||
|
}
|
||||||
|
}
|
||||||
ply.Filters = filters
|
ply.Filters = filters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
88
src/replication/source/label_filter.go
Normal file
88
src/replication/source/label_filter.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/common"
|
||||||
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
|
"github.com/vmware/harbor/src/replication"
|
||||||
|
"github.com/vmware/harbor/src/replication/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LabelFilter filter resources according to label
|
||||||
|
type LabelFilter struct {
|
||||||
|
labelID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init ...
|
||||||
|
func (l *LabelFilter) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConvertor ...
|
||||||
|
func (l *LabelFilter) GetConvertor() Convertor {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLabelFilter returns an instance of LabelFilter
|
||||||
|
func NewLabelFilter(labelID int64) *LabelFilter {
|
||||||
|
return &LabelFilter{
|
||||||
|
labelID: labelID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoFilter filter the resources according to the label
|
||||||
|
func (l *LabelFilter) DoFilter(items []models.FilterItem) []models.FilterItem {
|
||||||
|
candidates := []string{}
|
||||||
|
for _, item := range items {
|
||||||
|
candidates = append(candidates, item.Value)
|
||||||
|
}
|
||||||
|
log.Debugf("label filter candidates: %v", candidates)
|
||||||
|
result := []models.FilterItem{}
|
||||||
|
for _, item := range items {
|
||||||
|
hasLabel, err := hasLabel(item, l.labelID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to check the label of resouce %v: %v, skip it", item, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if hasLabel {
|
||||||
|
log.Debugf("has label %d, add %s to the label filter result list", l.labelID, item.Value)
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasLabel(resource models.FilterItem, labelID int64) (bool, error) {
|
||||||
|
rType := ""
|
||||||
|
switch resource.Kind {
|
||||||
|
case replication.FilterItemKindProject:
|
||||||
|
rType = common.ResourceTypeProject
|
||||||
|
case replication.FilterItemKindRepository:
|
||||||
|
rType = common.ResourceTypeRepository
|
||||||
|
case replication.FilterItemKindTag:
|
||||||
|
rType = common.ResourceTypeImage
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("invalid resource type: %s", resource.Kind)
|
||||||
|
}
|
||||||
|
rl, err := dao.GetResourceLabel(rType, resource.Value, labelID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return rl != nil, nil
|
||||||
|
}
|
48
src/replication/source/label_filter_test.go
Normal file
48
src/replication/source/label_filter_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/common/utils/test"
|
||||||
|
"github.com/vmware/harbor/src/replication"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/vmware/harbor/src/replication/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInitOfLabelFilter(t *testing.T) {
|
||||||
|
filter := NewLabelFilter(1)
|
||||||
|
assert.Nil(t, filter.Init())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConvertorOfLabelFilter(t *testing.T) {
|
||||||
|
filter := NewLabelFilter(1)
|
||||||
|
assert.Nil(t, filter.GetConvertor())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoFilterOfLabelFilter(t *testing.T) {
|
||||||
|
test.InitDatabaseFromEnv()
|
||||||
|
filter := NewLabelFilter(1)
|
||||||
|
items := []models.FilterItem{
|
||||||
|
models.FilterItem{
|
||||||
|
Kind: replication.FilterItemKindTag,
|
||||||
|
Value: "library/hello-world:latest",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result := filter.DoFilter(items)
|
||||||
|
assert.Equal(t, 0, len(result))
|
||||||
|
}
|
@ -76,8 +76,8 @@ func (r *RepositoryFilter) DoFilter(items []models.FilterItem) []models.FilterIt
|
|||||||
_, repository = utils.ParseRepository(repository)
|
_, repository = utils.ParseRepository(repository)
|
||||||
matched, err := match(r.pattern, repository)
|
matched, err := match(r.pattern, repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to match pattern %s to value %s: %v", r.pattern, repository, err)
|
log.Errorf("failed to match pattern %s to value %s: %v, skip it", r.pattern, repository, err)
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
if matched {
|
if matched {
|
||||||
log.Debugf("pattern %s matched, add %s to the repository filter result list", r.pattern, item.Value)
|
log.Debugf("pattern %s matched, add %s to the repository filter result list", r.pattern, item.Value)
|
||||||
|
@ -71,7 +71,7 @@ func (t *TagFilter) DoFilter(items []models.FilterItem) []models.FilterItem {
|
|||||||
tag := strings.SplitN(item.Value, ":", 2)[1]
|
tag := strings.SplitN(item.Value, ":", 2)[1]
|
||||||
matched, err := match(t.pattern, tag)
|
matched, err := match(t.pattern, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to match pattern %s to value %s: %v", t.pattern, tag, err)
|
log.Errorf("failed to match pattern %s to value %s: %v, skip it", t.pattern, tag, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user