Skip to content

Commit 12e6f2d

Browse files
refactor Kptfile-label filtering introduced in PR 375
- add spec.packageMetadata.labels to existing selectable-fields framework - add handling in fieldselector.go for "select key from map"-type selectors - handle them generically - pick out field name from square-bracket syntax and use this to fit them into existing selectable-fields framework - make field selectors' KptfileLabels a Kubernetes labels.Selector to make matching less reliant on bespoke map matching
1 parent 8a86a76 commit 12e6f2d

4 files changed

Lines changed: 50 additions & 49 deletions

File tree

api/porch/v1alpha1/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const (
4848
PkgRevSelectorRepository PkgRevFieldSelector = "spec.repository"
4949
PkgRevSelectorWorkspaceName PkgRevFieldSelector = "spec.workspaceName"
5050
PkgRevSelectorLifecycle PkgRevFieldSelector = "spec.lifecycle"
51+
PkgRevSelectorKptfileLabels PkgRevFieldSelector = "spec.packageMetadata.labels"
5152
)
5253

5354
var PackageRevisionSelectableFields = []PkgRevFieldSelector{
@@ -58,6 +59,7 @@ var PackageRevisionSelectableFields = []PkgRevFieldSelector{
5859
PkgRevSelectorRepository,
5960
PkgRevSelectorWorkspaceName,
6061
PkgRevSelectorLifecycle,
62+
PkgRevSelectorKptfileLabels,
6163
}
6264

6365
// PackageRevisionList

pkg/cache/dbcache/dbsqlfiltering.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
porchapi "github.com/nephio-project/porch/api/porch/v1alpha1"
2222
"github.com/nephio-project/porch/pkg/repository"
23+
"k8s.io/apimachinery/pkg/labels"
2324
)
2425

2526
func pkgListFilter2WhereClause(filter repository.ListPackageFilter) string {
@@ -134,15 +135,19 @@ func filter2SubClauseLifecycle(whereStatement string, filterField []porchapi.Pac
134135
}
135136
}
136137

137-
func filter2SubClauseKptfileLabels(whereStatement string, filterLabels map[string]string, first bool) (string, bool) {
138-
if len(filterLabels) == 0 {
138+
func filter2SubClauseKptfileLabels(whereStatement string, filterLabels labels.Selector, first bool) (string, bool) {
139+
if filterLabels == nil {
140+
return whereStatement, first
141+
}
142+
requirements, _ := filterLabels.Requirements()
143+
if len(requirements) == 0 {
139144
return whereStatement, first
140145
}
141146

142147
var subClauses []string
143-
for labelKey, labelValue := range filterLabels {
148+
for _, req := range requirements {
144149
subClause := fmt.Sprintf("(package_revisions.spec::jsonb->'packageMetadata'->'labels'->>'%s' = '%s')",
145-
labelKey, labelValue)
150+
req.Key(), req.Values().List()[0])
146151
subClauses = append(subClauses, subClause)
147152
}
148153

pkg/registry/porch/fieldselector.go

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
apierrors "k8s.io/apimachinery/pkg/api/errors"
2525
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
2626
"k8s.io/apimachinery/pkg/fields"
27+
"k8s.io/apimachinery/pkg/labels"
2728
"k8s.io/apimachinery/pkg/selection"
2829
)
2930

@@ -74,6 +75,15 @@ var (
7475
}
7576
return err
7677
},
78+
porchapi.PkgRevSelectorKptfileLabels: func(filter *repository.ListPackageRevisionFilter, labelRequirement string) error {
79+
parsedReq, err := labels.ParseToRequirements(labelRequirement)
80+
if err != nil {
81+
return apierrors.NewBadRequest(err.Error())
82+
}
83+
84+
filter.KptfileLabels = filter.KptfileLabels.Add(parsedReq[0])
85+
return nil
86+
},
7787
}
7888
)
7989

@@ -93,18 +103,14 @@ func convertPackageFieldSelector(label, value string) (internalLabel, internalVa
93103

94104
// convertPackageRevisionFieldSelector is the schema conversion function for normalizing the FieldSelector for PackageRevision
95105
func convertPackageRevisionFieldSelector(label, value string) (internalLabel, internalValue string, err error) {
96-
if slices.Contains(porchapi.PackageRevisionSelectableFields, porchapi.PkgRevFieldSelector(label)) {
97-
return label, value, nil
98-
}
99-
100-
if strings.HasPrefix(label, "spec.packageMetadata.labels[") && strings.HasSuffix(label, "]") {
101-
start := len("spec.packageMetadata.labels[")
102-
end := len(label) - 1
103-
labelKey := label[start:end]
104-
if labelKey == "" {
106+
mapSelectorSplit := strings.Split(label, "[")
107+
if len(mapSelectorSplit) > 1 {
108+
if labelKey := mapSelectorSplit[1]; labelKey == "]" {
105109
return "", "", fmt.Errorf("label key cannot be empty in field selector %q", label)
106110
}
111+
}
107112

113+
if isSelectable := slices.Contains(porchapi.PackageRevisionSelectableFields, porchapi.PkgRevFieldSelector(mapSelectorSplit[0])); isSelectable {
108114
return label, value, nil
109115
}
110116

@@ -159,8 +165,9 @@ func parsePackageFieldSelector(fieldSelector fields.Selector) (repository.ListPa
159165
// parsePackageRevisionFieldSelector parses client-provided fields.Selector into a ListPackageRevisionFilter
160166
func parsePackageRevisionFieldSelector(options *metainternalversion.ListOptions) (*repository.ListPackageRevisionFilter, error) {
161167
filter := &repository.ListPackageRevisionFilter{
162-
Label: options.LabelSelector,
163-
Key: repository.PackageRevisionKey{},
168+
Label: options.LabelSelector,
169+
Key: repository.PackageRevisionKey{},
170+
KptfileLabels: labels.Everything(),
164171
}
165172

166173
fieldSelector := options.FieldSelector
@@ -178,23 +185,10 @@ func parsePackageRevisionFieldSelector(options *metainternalversion.ListOptions)
178185
return filter, apierrors.NewBadRequest(fmt.Sprintf("unsupported fieldSelector operator %q for field %q", requirement.Operator, requirement.Field))
179186
}
180187

181-
if strings.HasPrefix(requirement.Field, "spec.packageMetadata.labels[") && strings.HasSuffix(requirement.Field, "]") {
182-
start := len("spec.packageMetadata.labels[")
183-
end := len(requirement.Field) - 1
184-
labelKey := requirement.Field[start:end]
185-
186-
if labelKey == "" {
187-
return filter, apierrors.NewBadRequest(fmt.Sprintf("label key cannot be empty in field selector %q", requirement.Field))
188-
}
189-
190-
if filter.KptfileLabels == nil {
191-
filter.KptfileLabels = make(map[string]string)
192-
}
193-
194-
filter.KptfileLabels[labelKey] = requirement.Value
195-
196-
// skip the upcoming requirement.Field check
197-
return filter, nil
188+
mapSelectorSplit := strings.Split(requirement.Field, "[")
189+
if len(mapSelectorSplit) > 1 {
190+
requirement.Field = mapSelectorSplit[0]
191+
requirement.Value = strings.Split(mapSelectorSplit[1], "]")[0] + "=" + requirement.Value
198192
}
199193

200194
filteredField := porchapi.PkgRevFieldSelector(requirement.Field)

pkg/repository/repository.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ type ListPackageRevisionFilter struct {
330330
Lifecycles []porchapi.PackageRevisionLifecycle
331331

332332
// KptfileLabels matches labels specified in the Kptfile
333-
KptfileLabels map[string]string
333+
KptfileLabels labels.Selector
334334

335335
Label labels.Selector
336336
}
@@ -345,22 +345,8 @@ func (f *ListPackageRevisionFilter) Matches(ctx context.Context, p PackageRevisi
345345
return false
346346
}
347347

348-
if len(f.KptfileLabels) > 0 {
349-
packageRevision, err := p.GetPackageRevision(ctx)
350-
if err != nil {
351-
return false
352-
}
353-
354-
if packageRevision.Spec.PackageMetadata == nil {
355-
return false
356-
}
357-
358-
for labelKey, expectedlValue := range f.KptfileLabels {
359-
actualValue, exists := packageRevision.Spec.PackageMetadata.Labels[labelKey]
360-
if !exists || actualValue != expectedlValue {
361-
return false
362-
}
363-
}
348+
if !f.MatchesKptfileLabels(ctx, p) {
349+
return false
364350
}
365351

366352
if !f.MatchesLabels(ctx, p) {
@@ -390,6 +376,20 @@ func (f *ListPackageRevisionFilter) MatchesLabels(ctx context.Context, p Package
390376
return true
391377
}
392378

379+
// MatchesKptfileLabels returns true if the filter either:
380+
// - does not filter on labels in package metadata (nil KptfileLabels field), OR
381+
// - matches on labels in the Kptfile of the resources of the provided PackageRevision
382+
func (f *ListPackageRevisionFilter) MatchesKptfileLabels(ctx context.Context, p PackageRevision) bool {
383+
if f.KptfileLabels != nil {
384+
packageRevision, err := p.GetPackageRevision(ctx)
385+
if err != nil || packageRevision.Spec.PackageMetadata == nil {
386+
return f.KptfileLabels.Matches(labels.Set(packageRevision.Spec.PackageMetadata.Labels))
387+
}
388+
}
389+
390+
return true
391+
}
392+
393393
// getPkgRevLabels returns the metadata labels of a given PackageRevision for filtering purposes.
394394
// The labels are returned in the form of a Kubernetes labels.Set which can be easily matched
395395
// against a labels.Selector which came in in a list request.

0 commit comments

Comments
 (0)