Skip to content

Commit

Permalink
feat(api-server): contain matches on name and tags (kumahq#5606)
Browse files Browse the repository at this point in the history
The api now takes `?name` to filter on subset of the dpp name
it also works on subset for tag value

Signed-off-by: Charly Molter <charly.molter@konghq.com>
Signed-off-by: Bart Smykla <bartek@smykla.com>
  • Loading branch information
lahabana authored and bartsmykla committed Jan 14, 2023
1 parent c45b7da commit 35df0ef
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 135 deletions.
51 changes: 29 additions & 22 deletions api/mesh/v1alpha1/dataplane_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,28 +189,27 @@ func (n *Dataplane_Networking) GetHealthyInbounds() (inbounds []*Dataplane_Netwo

// Matches is simply an alias for MatchTags to make source code more aesthetic.
func (d *Dataplane) Matches(selector TagSelector) bool {
if d != nil {
return d.MatchTags(selector)
if d == nil {
return false
}
return false
}

func (d *Dataplane) MatchTags(selector TagSelector) bool {
for _, inbound := range d.GetNetworking().GetInbound() {
if inbound.MatchTags(selector) {
if selector.Matches(inbound.Tags) {
return true
}
}
if d.GetNetworking().GetGateway() != nil {
if d.Networking.Gateway.MatchTags(selector) {
return selector.Matches(d.GetNetworking().GetGateway().GetTags())
}

func (d *Dataplane) MatchTagsFuzzy(selector TagSelector) bool {
if d == nil {
return false
}
for _, inbound := range d.GetNetworking().GetInbound() {
if selector.MatchesFuzzy(inbound.Tags) {
return true
}
}
return false
}

func (d *Dataplane_Networking_Gateway) MatchTags(selector TagSelector) bool {
return selector.Matches(d.Tags)
return selector.MatchesFuzzy(d.GetNetworking().GetGateway().GetTags())
}

// GetService returns a service represented by this inbound interface.
Expand All @@ -235,14 +234,6 @@ func (d *Dataplane_Networking_Inbound) GetProtocol() string {
return d.Tags[ProtocolTag]
}

func (d *Dataplane_Networking_Inbound) MatchTags(selector TagSelector) bool {
return selector.Matches(d.Tags)
}

func (d *Dataplane_Networking_Outbound) MatchTags(selector TagSelector) bool {
return selector.Matches(d.GetTagsIncludingLegacy())
}

// GetTagsIncludingLegacy returns tags but taking on account old legacy format of "kuma.io/service" field in outbound
// Remove it and migrate to GetTags() once "kuma.io/service" field is removed.
func (d *Dataplane_Networking_Outbound) GetTagsIncludingLegacy() map[string]string {
Expand Down Expand Up @@ -274,6 +265,22 @@ func (s TagSelector) Matches(tags map[string]string) bool {
return true
}

func (s TagSelector) MatchesFuzzy(tags map[string]string) bool {
if len(s) == 0 {
return true
}
for tag, value := range s {
inboundVal, exist := tags[tag]
if !exist {
return false
}
if !strings.Contains(inboundVal, value) && value != MatchAllTag {
return false
}
}
return true
}

func (s TagSelector) Rank() (r TagSelectorRank) {
for _, value := range s {
if value == MatchAllTag {
Expand Down
89 changes: 0 additions & 89 deletions api/mesh/v1alpha1/dataplane_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,49 +180,6 @@ var _ = Describe("Dataplane_Networking", func() {
})
})

var _ = Describe("Dataplane_Networking_Outbound", func() {
type testCase struct {
serviceTag string
selector TagSelector
expectedMatch bool
}
DescribeTable("MatchTags()",
func(given testCase) {
// given
outbound := Dataplane_Networking_Outbound{
Service: given.serviceTag,
}

// when
matched := outbound.MatchTags(given.selector)

// then
Expect(matched).To(Equal(given.expectedMatch))
},
Entry("it should match *", testCase{
serviceTag: "backend",
selector: map[string]string{
"kuma.io/service": "*",
},
expectedMatch: true,
}),
Entry("it should match service", testCase{
serviceTag: "backend",
selector: map[string]string{
"kuma.io/service": "backend",
},
expectedMatch: true,
}),
Entry("it shouldn't match tag other than service", testCase{
serviceTag: "backend",
selector: map[string]string{
"version": "1.0",
},
expectedMatch: false,
}),
)
})

var _ = Describe("Dataplane_Networking_Inbound", func() {

Describe("GetService()", func() {
Expand Down Expand Up @@ -326,29 +283,6 @@ var _ = Describe("Dataplane with inbound", func() {
Expect(tags.Values("role")).To(Equal([]string{"metrics"}))
})
})

Describe("MatchTags()", func() {
It("should match any inbound", func() {
// when
selector := TagSelector{
"kuma.io/service": "backend",
"version": "v1",
}

// then
Expect(d.MatchTags(selector)).To(BeTrue())
})

It("should not match if all inbounds did not match", func() {
// when
selector := TagSelector{
"kuma.io/service": "unknown",
}

// then
Expect(d.MatchTags(selector)).To(BeFalse())
})
})
})

var _ = Describe("Dataplane classification", func() {
Expand Down Expand Up @@ -424,29 +358,6 @@ var _ = Describe("Dataplane with gateway", func() {
Expect(tags.Values("kuma.io/service")).To(Equal([]string{"backend"}))
})
})

Describe("MatchTags()", func() {
It("should match gateway", func() {
// when
selector := TagSelector{
"kuma.io/service": "backend",
"version": "v1",
}

// then
Expect(d.MatchTags(selector)).To(BeTrue())
})

It("should not match if gateway did not match", func() {
// when
selector := TagSelector{
"kuma.io/service": "unknown",
}

// then
Expect(d.MatchTags(selector)).To(BeFalse())
})
})
})

var _ = Describe("TagSelector", func() {
Expand Down
8 changes: 4 additions & 4 deletions pkg/api-server/dataplane_overview_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ func (r *dataplaneOverviewEndpoints) inspectDataplanes(request *restful.Request,
rest_errors.HandleError(response, err, "Could not retrieve dataplane overviews")
return
}
namePrefix := request.QueryParameter("namePrefix")
nameContains := request.QueryParameter("name")

overviews, err := r.fetchOverviews(request.Request.Context(), page, meshName, namePrefix, filter)
overviews, err := r.fetchOverviews(request.Request.Context(), page, meshName, nameContains, filter)
if err != nil {
rest_errors.HandleError(response, err, "Could not retrieve dataplane overviews")
return
Expand All @@ -126,9 +126,9 @@ func (r *dataplaneOverviewEndpoints) inspectDataplanes(request *restful.Request,
}
}

func (r *dataplaneOverviewEndpoints) fetchOverviews(ctx context.Context, p page, meshName string, namePrefix string, filter store.ListFilterFunc) (mesh.DataplaneOverviewResourceList, error) {
func (r *dataplaneOverviewEndpoints) fetchOverviews(ctx context.Context, p page, meshName string, nameContains string, filter store.ListFilterFunc) (mesh.DataplaneOverviewResourceList, error) {
dataplanes := mesh.DataplaneResourceList{}
if err := r.resManager.List(ctx, &dataplanes, store.ListByMesh(meshName), store.ListByPage(p.size, p.offset), store.ListByFilterFunc(filter), store.ListByNamePrefix(namePrefix)); err != nil {
if err := r.resManager.List(ctx, &dataplanes, store.ListByMesh(meshName), store.ListByPage(p.size, p.offset), store.ListByFilterFunc(filter), store.ListByNameContains(nameContains)); err != nil {
return mesh.DataplaneOverviewResourceList{}, err
}

Expand Down
10 changes: 9 additions & 1 deletion pkg/api-server/dataplane_overview_endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ var _ = Describe("Dataplane Overview Endpoints", func() {
url: "/meshes/mesh1/dataplanes+insights?tag=service:backend",
expectedJson: fmt.Sprintf(`{"total": 1, "items": [%s], "next": null}`, dp1Json),
}),
Entry("should list with only subset tag", testCase{
url: "/meshes/mesh1/dataplanes+insights?tag=service:ck",
expectedJson: fmt.Sprintf(`{"total": 1, "items": [%s], "next": null}`, dp1Json),
}),
Entry("should list all with all matching tags", testCase{
url: "/meshes/mesh1/dataplanes+insights?tag=service:backend&tag=version:v1",
expectedJson: fmt.Sprintf(`{"total": 1, "items": [%s], "next": null}`, dp1Json),
Expand All @@ -278,7 +282,11 @@ var _ = Describe("Dataplane Overview Endpoints", func() {
expectedJson: fmt.Sprintf(`{"total": 1, "items": [%s], "next": null}`, gatewayDelegatedJson),
}),
Entry("should list only dataplanes that starts with gateway", testCase{
url: "/meshes/mesh1/dataplanes+insights?namePrefix=gateway",
url: "/meshes/mesh1/dataplanes+insights?name=gateway",
expectedJson: fmt.Sprintf(`{"total": 2, "items": [%s, %s], "next": null}`, gatewayBuiltinJson, gatewayDelegatedJson),
}),
Entry("should list only dataplanes that contains with tew", testCase{
url: "/meshes/mesh1/dataplanes+insights?name=tew",
expectedJson: fmt.Sprintf(`{"total": 2, "items": [%s, %s], "next": null}`, gatewayBuiltinJson, gatewayDelegatedJson),
}),
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/api-server/filtering.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func genFilter(request *restful.Request) (store.ListFilterFunc, error) {
return false
}

if !dataplane.Spec.MatchTags(tags) {
if !dataplane.Spec.MatchTagsFuzzy(tags) {
return false
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/core/permissions/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func MatchExternalServicesTrafficPermissions(
}
matched := false
for _, selector := range permission.Spec.Sources {
if dataplane.Spec.MatchTags(selector.Match) {
if dataplane.Spec.Matches(selector.Match) {
matched = true
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/policy/dataplane_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func SelectInboundDataplanePolicies(dataplane *mesh.DataplaneResource, policies
continue
}
tagSelector := mesh_proto.TagSelector(selector.Match)
if inbound.MatchTags(tagSelector) {
if tagSelector.Matches(inbound.Tags) {
rank := tagSelector.Rank()
if rank.CompareTo(bestRank) > 0 || sameRankCreatedLater(policy, rank) {
bestRank = rank
Expand Down
16 changes: 8 additions & 8 deletions pkg/core/resources/store/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,12 @@ func (g *GetOptions) HashCode() string {
type ListFilterFunc func(rs core_model.Resource) bool

type ListOptions struct {
Mesh string
PageSize int
PageOffset string
FilterFunc ListFilterFunc
NamePrefix string
Ordered bool
Mesh string
PageSize int
PageOffset string
FilterFunc ListFilterFunc
NameContains string
Ordered bool
}

type ListOptionsFunc func(*ListOptions)
Expand All @@ -180,9 +180,9 @@ func (l *ListOptions) Filter(rs core_model.Resource) bool {
return l.FilterFunc(rs)
}

func ListByNamePrefix(name string) ListOptionsFunc {
func ListByNameContains(name string) ListOptionsFunc {
return func(opts *ListOptions) {
opts.NamePrefix = name
opts.NameContains = name
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/plugins/resources/k8s/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func (s *KubernetesStore) List(ctx context.Context, rs core_model.ResourceList,
if opts.Mesh != "" && r.GetMeta().GetMesh() != opts.Mesh {
return false
}
if opts.NamePrefix != "" && !strings.HasPrefix(r.GetMeta().GetName(), opts.NamePrefix) {
if opts.NameContains != "" && !strings.Contains(r.GetMeta().GetName(), opts.NameContains) {
return false
}
return true
Expand Down
6 changes: 3 additions & 3 deletions pkg/plugins/resources/memory/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ func (c *memoryStore) List(_ context.Context, rs core_model.ResourceList, fs ...

opts := store.NewListOptions(fs...)

records := c.findRecords(string(rs.GetItemType()), opts.Mesh, opts.NamePrefix)
records := c.findRecords(string(rs.GetItemType()), opts.Mesh, opts.NameContains)

for i := 0; i < len(records); i++ {
r := rs.NewItem()
Expand All @@ -289,7 +289,7 @@ func (c *memoryStore) findRecord(
return -1, nil
}

func (c *memoryStore) findRecords(resourceType string, mesh string, prefix string) []*memoryStoreRecord {
func (c *memoryStore) findRecords(resourceType string, mesh string, contains string) []*memoryStoreRecord {
res := make([]*memoryStoreRecord, 0)
for _, rec := range c.records {
if rec.ResourceType != resourceType {
Expand All @@ -298,7 +298,7 @@ func (c *memoryStore) findRecords(resourceType string, mesh string, prefix strin
if mesh != "" && rec.Mesh != mesh {
continue
}
if prefix != "" && !strings.HasPrefix(rec.Name, prefix) {
if contains != "" && !strings.Contains(rec.Name, contains) {
continue
}
res = append(res, rec)
Expand Down
4 changes: 2 additions & 2 deletions pkg/plugins/resources/postgres/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ func (r *postgresResourceStore) List(_ context.Context, resources core_model.Res
statement += fmt.Sprintf(" AND mesh=$%d", argsIndex)
statementArgs = append(statementArgs, opts.Mesh)
}
if opts.NamePrefix != "" {
if opts.NameContains != "" {
argsIndex++
statement += fmt.Sprintf(" AND name LIKE $%d", argsIndex)
statementArgs = append(statementArgs, opts.NamePrefix+"%")
statementArgs = append(statementArgs, "%"+opts.NameContains+"%")
}
statement += " ORDER BY name, mesh"

Expand Down
4 changes: 2 additions & 2 deletions pkg/test/store/store_test_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ func ExecuteStoreTests(
list := core_mesh.TrafficRouteResourceList{}

// when
err := s.List(context.Background(), &list, store.ListByNamePrefix("list-res"))
err := s.List(context.Background(), &list, store.ListByNameContains("list-res"))

// then
Expect(err).ToNot(HaveOccurred())
Expand All @@ -373,7 +373,7 @@ func ExecuteStoreTests(
list := core_mesh.TrafficRouteResourceList{}

// when
err := s.List(context.Background(), &list, store.ListByNamePrefix("list-res"), store.ListByMesh(mesh))
err := s.List(context.Background(), &list, store.ListByNameContains("list-res"), store.ListByMesh(mesh))

// then
Expect(err).ToNot(HaveOccurred())
Expand Down

0 comments on commit 35df0ef

Please sign in to comment.