Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rules): support Labels with SectionName in ResolveTargetRef function #12743

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/core/resources/apis/core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package core
type Port interface {
GetName() string
GetValue() uint32
GetNameOrStringifyPort() string
}
143 changes: 88 additions & 55 deletions pkg/plugins/policies/core/rules/common/resolvetargetref.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (
common_api "github.com/kumahq/kuma/api/common/v1alpha1"
mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
"github.com/kumahq/kuma/pkg/core/resources/apis/core"
meshservice_api "github.com/kumahq/kuma/pkg/core/resources/apis/meshservice/api/v1alpha1"
core_model "github.com/kumahq/kuma/pkg/core/resources/model"
"github.com/kumahq/kuma/pkg/plugins/policies/core/rules/subsetutils"
"github.com/kumahq/kuma/pkg/util/pointer"
)

type ResourceSection struct {
Expand All @@ -35,82 +35,115 @@ type ResourceWithPorts interface {
GetPorts() []core.Port
}

type query struct {
byIdentifier *core_model.ResourceIdentifier
byLabels map[string]string
port uint32
sectionName string
}

func (q query) findPort(ports []core.Port) core.Port {
switch {
case q.port != 0:
for _, port := range ports {
if port.GetValue() == q.port {
return port
}
}
case q.sectionName != "":
slonka marked this conversation as resolved.
Show resolved Hide resolved
for _, port := range ports {
if port.GetName() == q.sectionName {
return port
}
if parsed, ok := tryParsePort(q.sectionName); ok && port.GetName() == "" && port.GetValue() == parsed {
return port
}
}
}
return nil
}

func ResolveTargetRef(targetRef common_api.TargetRef, tMeta core_model.ResourceMeta, reader ResourceReader) []*ResourceSection {
if !targetRef.Kind.IsRealResource() {
return nil
}
rtype := core_model.ResourceType(targetRef.Kind)
list := reader.ListOrEmpty(rtype).GetItems()

var implicitPort uint32
implicitLabels := map[string]string{}
if targetRef.Kind == common_api.MeshService && targetRef.SectionName == "" {
if name, namespace, port, err := parseService(targetRef.Name); err == nil {
implicitLabels[mesh_proto.KubeNamespaceTag] = namespace
implicitLabels[mesh_proto.DisplayName] = name
implicitPort = port
// targetRef to query
var q query
switch {
case len(targetRef.Labels) > 0:
q = query{
byLabels: targetRef.Labels,
sectionName: targetRef.SectionName,
}
default:
q = query{
byIdentifier: pointer.To(core_model.TargetRefToResourceIdentifier(tMeta, targetRef)),
sectionName: targetRef.SectionName,
}
}

labels := targetRef.Labels
if len(implicitLabels) > 0 {
labels = implicitLabels
// backwards compatibility, we want old policies with targetRef{kind:MeshService,name:backend_kuma-demo_svc_8080}
// to resolve to new MeshService backend in the kuma-demo namespace on port 8080
if targetRef.Kind == common_api.MeshService && targetRef.SectionName == "" {
if name, namespace, port, err := parseService(targetRef.Name); err == nil {
q = query{
byLabels: map[string]string{
mesh_proto.KubeNamespaceTag: namespace,
mesh_proto.DisplayName: name,
},
port: port,
}
}
}

if len(labels) > 0 {
var rv []*ResourceSection
trLabels := subsetutils.NewSubset(labels)
// resolve query without taking port/sectionName into account
rtype := core_model.ResourceType(targetRef.Kind)
var resources []core_model.Resource
switch {
case q.byLabels != nil:
list := reader.ListOrEmpty(rtype).GetItems()
trLabels := subsetutils.NewSubset(q.byLabels)
for _, r := range list {
rLabels := subsetutils.NewSubset(r.GetMeta().GetLabels())
var implicitSectionName string
if ms, ok := r.(*meshservice_api.MeshServiceResource); ok && implicitPort != 0 {
for _, port := range ms.Spec.Ports {
if port.Port == implicitPort {
implicitSectionName = port.Name
}
}
}
sn := targetRef.SectionName
if sn == "" {
sn = implicitSectionName
}
if trLabels.IsSubset(rLabels) {
rv = append(rv, &ResourceSection{
Resource: r,
SectionName: sn,
})
resources = append(resources, r)
}
}
return rv
case q.byIdentifier != nil:
if r := reader.Get(rtype, *q.byIdentifier); r != nil {
resources = []core_model.Resource{r}
}
}

ri := core_model.TargetRefToResourceIdentifier(tMeta, targetRef)
resolvedResource := reader.Get(rtype, ri)
if resolvedResource == nil {
return nil
if len(resources) == 0 {
return []*ResourceSection{}
}

if resourceWithPorts, ok := resolvedResource.(ResourceWithPorts); ok && targetRef.SectionName != "" {
for _, port := range resourceWithPorts.GetPorts() {
if port.GetName() == targetRef.SectionName {
return []*ResourceSection{{
Resource: resolvedResource,
SectionName: port.GetName(),
}}
}
if targetRefPort, ok := tryParsePort(targetRef.SectionName); ok && port.GetName() == "" && port.GetValue() == targetRefPort {
return []*ResourceSection{{
Resource: resolvedResource,
SectionName: targetRef.SectionName,
}}
if q.port == 0 && q.sectionName == "" {
result := make([]*ResourceSection, len(resources))
for i := range resources {
result[i] = &ResourceSection{Resource: resources[i]}
}
return result
}

// filter out resources that don't have requested section name or port
slonka marked this conversation as resolved.
Show resolved Hide resolved
var result []*ResourceSection
for _, r := range resources {
if resourceWithPorts, ok := r.(ResourceWithPorts); ok {
if port := q.findPort(resourceWithPorts.GetPorts()); port != nil {
result = append(result, &ResourceSection{
Resource: r,
SectionName: port.GetNameOrStringifyPort(),
})
}
} else {
result = append(result, &ResourceSection{Resource: r})
}
return []*ResourceSection{}
}

return []*ResourceSection{{
Resource: resolvedResource,
}}
return result
}

func tryParsePort(s string) (uint32, bool) {
Expand All @@ -136,7 +169,7 @@ func parseService(host string) (string, string, uint32, error) {
case 3:
// service less service names have no port, so we just put the reserved
// one here to note that this service is actually
port = mesh_proto.TCPPortReserved
port = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems unrelated 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function parseService was copied from here outbound_converter.go. When a port is not specified, i.e. backend_kuma-demo_svc it returned reserved port value 49151.

I don't really know why we need this reserved value in outbound_converter.go, but in resolvedtargetref.go I didn't want to introduce extra handling of special values especially when value 0 works perfectly fine for this case. Otherwise, I'd have to add here something like

- if q.port == 0 && q.sectionName == "" {
+ if (q.port == 0 || q.port == mesh_proto.TCPPortReserved) && q.sectionName == "" {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we somehow document that it was copied? or maybe we should make it DRY?

default:
return "", "", 0, errors.Errorf("service tag in unexpected format")
}
Expand Down
Loading
Loading