Skip to content

Commit

Permalink
refactor(rules): organize code in the packages, prepare for inbound r…
Browse files Browse the repository at this point in the history
…ules (#12559)

## Motivation

* with the introduction of inbound rules, a single file `rules.go` is
insufficient
* new inbound rules are going to introduce a new entry type (i.e.
`to[i]` has `targetRef` and `default` fields, while `rules[i]` is going
to have `default`, `matches`, `targetRef` fields). It's easier to use
generics, like
    ```go
    type WithPolicyAttributes[T any] struct {
    	Entry T

    	TopLevel  common_api.TargetRef
    	Meta      core_model.ResourceMeta
    	RuleIndex int
    }
    ```
to share the code between inbound and outbound rules (see packages
`merge`, `sort`, `common`)

Signed-off-by: Ilya Lobkov <[email protected]>
  • Loading branch information
lobkovilya authored Jan 20, 2025
1 parent a00b2e0 commit 820494c
Show file tree
Hide file tree
Showing 213 changed files with 1,521 additions and 1,237 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,6 @@ issues:
- linters:
- staticcheck
text: "SA1019: .* is deprecated: use FullResyncInterval instead"
- linters:
- staticcheck
text: "SA1019: .* is deprecated: use common.WithPolicyAttributes instead"
7 changes: 4 additions & 3 deletions pkg/api-server/oapi-helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package oapi_helpers
import (
api_common "github.com/kumahq/kuma/api/openapi/types/common"
core_model "github.com/kumahq/kuma/pkg/core/resources/model"
core_rules "github.com/kumahq/kuma/pkg/plugins/policies/core/rules"
rules_common "github.com/kumahq/kuma/pkg/plugins/policies/core/rules/common"
"github.com/kumahq/kuma/pkg/plugins/policies/core/rules/subsetutils"
"github.com/kumahq/kuma/pkg/util/pointer"
)

Expand Down Expand Up @@ -34,15 +35,15 @@ func ResourceMetaListToMetaList(resType core_model.ResourceType, in []core_model
return out
}

func SubsetToRuleMatcher(subset core_rules.Subset) []api_common.RuleMatcher {
func SubsetToRuleMatcher(subset subsetutils.Subset) []api_common.RuleMatcher {
matchers := []api_common.RuleMatcher{}
for _, m := range subset {
matchers = append(matchers, api_common.RuleMatcher{Key: m.Key, Value: m.Value, Not: m.Not})
}
return matchers
}

func OriginListToResourceRuleOrigin(resType core_model.ResourceType, origins []core_rules.Origin) []api_common.ResourceRuleOrigin {
func OriginListToResourceRuleOrigin(resType core_model.ResourceType, origins []rules_common.Origin) []api_common.ResourceRuleOrigin {
var out []api_common.ResourceRuleOrigin
for _, o := range origins {
out = append(out, api_common.ResourceRuleOrigin{
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/xds/inspect/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func getOutboundRuleAttachments(rules core_rules.Rules, networking *mesh_proto.D
}
attachment := byUniqueClusterName[name]
if attachment == nil {
computedRule := rules.Compute(core_rules.Element(outboundTags))
computedRule := rules.Compute(outboundTags)
if computedRule == nil {
continue
}
Expand Down
8 changes: 2 additions & 6 deletions pkg/plugins/policies/core/matchers/egress.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
core_model "github.com/kumahq/kuma/pkg/core/resources/model"
core_xds "github.com/kumahq/kuma/pkg/core/xds"
core_rules "github.com/kumahq/kuma/pkg/plugins/policies/core/rules"
"github.com/kumahq/kuma/pkg/plugins/policies/core/rules/outbound"
xds_context "github.com/kumahq/kuma/pkg/xds/context"
)

Expand Down Expand Up @@ -184,12 +185,7 @@ func processToRules(tags map[string]string, policies []core_model.Resource) (cor
}

func processToResourceRules(policies []core_model.Resource, resources xds_context.Resources) (core_rules.ToRules, error) {
toList, err := core_rules.BuildToList(policies, resources)
if err != nil {
return core_rules.ToRules{}, err
}

resourceRules, err := core_rules.BuildResourceRules(toList, resources)
resourceRules, err := outbound.BuildRules(policies, resources)
if err != nil {
return core_rules.ToRules{}, err
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/plugins/policies/core/rules/common/cast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package common

import core_model "github.com/kumahq/kuma/pkg/core/resources/model"

// Cast attempts to cast a slice of core_model.Resource to a slice of a specific type T.
// It returns the casted slice and a boolean indicating whether the cast was successful.
// If any element in the slice cannot be cast to the specified type, the function returns nil and false.
func Cast[T any](rs []core_model.Resource) ([]T, bool) {
var rv []T
for _, r := range rs {
if casted, ok := r.GetSpec().(T); !ok {
return nil, false
} else {
rv = append(rv, casted)
}
}
return rv, true
}
69 changes: 69 additions & 0 deletions pkg/plugins/policies/core/rules/common/origin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package common

import (
common_api "github.com/kumahq/kuma/api/common/v1alpha1"
core_model "github.com/kumahq/kuma/pkg/core/resources/model"
meshhttproute_api "github.com/kumahq/kuma/pkg/plugins/policies/meshhttproute/api/v1alpha1"
meshtcproute_api "github.com/kumahq/kuma/pkg/plugins/policies/meshtcproute/api/v1alpha1"
)

type Origin struct {
Resource core_model.ResourceMeta
// RuleIndex is an index in the 'to[]' array, so we could unambiguously detect what to-item contributed to the final conf.
// Especially useful when to-item uses `targetRef.Labels`, because there is no obvious matching between the specific resource
// in `ResourceRule.Resource` and to-item.
RuleIndex int
}

type BackendRefOriginIndex map[common_api.MatchesHash]int

var EmptyMatches common_api.MatchesHash = ""

func (originIndex BackendRefOriginIndex) Update(conf interface{}, newIndex int) {
switch conf := conf.(type) {
case meshtcproute_api.Rule:
if conf.Default.BackendRefs != nil {
originIndex[EmptyMatches] = newIndex
}
case meshhttproute_api.PolicyDefault:
for _, rule := range conf.Rules {
if rule.Default.BackendRefs != nil {
hash := meshhttproute_api.HashMatches(rule.Matches)
originIndex[hash] = newIndex
}
}
default:
return
}
}

func Origins[B BaseEntry, T interface {
PolicyAttributes
Entry[B]
}](items []T, withRuleIndex bool) ([]Origin, BackendRefOriginIndex) {
var rv []Origin

type keyType struct {
core_model.ResourceKey
ruleIndex int
}
key := func(policyItem T) keyType {
k := keyType{
ResourceKey: core_model.MetaToResourceKey(policyItem.GetResourceMeta()),
}
if withRuleIndex {
k.ruleIndex = policyItem.GetRuleIndex()
}
return k
}
set := map[keyType]struct{}{}
originIndex := BackendRefOriginIndex{}
for _, item := range items {
if _, ok := set[key(item)]; !ok {
originIndex.Update(item.GetEntry().GetDefault(), len(rv))
rv = append(rv, Origin{Resource: item.GetResourceMeta(), RuleIndex: item.GetRuleIndex()})
set[key(item)] = struct{}{}
}
}
return rv, originIndex
}
57 changes: 57 additions & 0 deletions pkg/plugins/policies/core/rules/common/policyattributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package common

import (
common_api "github.com/kumahq/kuma/api/common/v1alpha1"
core_model "github.com/kumahq/kuma/pkg/core/resources/model"
)

type PolicyAttributes interface {
GetTopLevel() common_api.TargetRef
GetResourceMeta() core_model.ResourceMeta
GetRuleIndex() int
}

// Entry is a piece of configuration that is part of a policy. Outbound policies, for example, have a list of entries called 'to'.
// Inbound policies at this moment have a list of entries called 'from'. Entries in 'from' and 'to' have the same type:
//
// type PolicyItem interface {
// GetTargetRef() common_api.TargetRef
// GetDefault() interface{}
// }
//
// But 'from' list of entries is going to be replaced with 'rules' according to docs/madr/decisions/069-inbound-policies.md.
// Entries in 'to' and entries in 'rules' won't have the same type anymore, they're going to have 'ToEntry' and 'RuleEntry'
// types respectively. So, we need to make 'Entry' generic to be able to use it in shared packages like 'sort', 'merge' and 'common'.
type Entry[T BaseEntry] interface {
GetEntry() T
}

// BaseEntry is a base interface for all entries in policies. Regardless of the type of the entry,
// it should always contain a piece of configuration.
type BaseEntry interface {
GetDefault() interface{}
}

type WithPolicyAttributes[T any] struct {
Entry T

TopLevel common_api.TargetRef
Meta core_model.ResourceMeta
RuleIndex int
}

func (p WithPolicyAttributes[T]) GetTopLevel() common_api.TargetRef {
return p.TopLevel
}

func (p WithPolicyAttributes[T]) GetResourceMeta() core_model.ResourceMeta {
return p.Meta
}

func (p WithPolicyAttributes[T]) GetRuleIndex() int {
return p.RuleIndex
}

func (p WithPolicyAttributes[T]) GetEntry() T {
return p.Entry
}
115 changes: 115 additions & 0 deletions pkg/plugins/policies/core/rules/common/resolvetargetref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package common

import (
"strconv"
"strings"

"github.com/pkg/errors"

common_api "github.com/kumahq/kuma/api/common/v1alpha1"
mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
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"
)

type ResourceSection struct {
Resource core_model.Resource
SectionName string
}

func (rs *ResourceSection) Identifier() core_model.TypedResourceIdentifier {
return UniqueKey(rs.Resource, rs.SectionName)
}

func UniqueKey(r core_model.Resource, sectionName string) core_model.TypedResourceIdentifier {
return core_model.TypedResourceIdentifier{
ResourceIdentifier: core_model.NewResourceIdentifier(r),
ResourceType: r.Descriptor().Name,
SectionName: sectionName,
}
}

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
}
}

labels := targetRef.Labels
if len(implicitLabels) > 0 {
labels = implicitLabels
}

if len(labels) > 0 {
var rv []*ResourceSection
trLabels := subsetutils.NewSubset(labels)
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,
})
}
}
return rv
}

ri := core_model.TargetRefToResourceIdentifier(tMeta, targetRef)
if resource := reader.Get(rtype, ri); resource != nil {
return []*ResourceSection{{
Resource: resource,
SectionName: targetRef.SectionName,
}}
}

return nil
}

func parseService(host string) (string, string, uint32, error) {
// split host into <name>_<namespace>_svc_<port>
segments := strings.Split(host, "_")

var port uint32
switch len(segments) {
case 4:
p, err := strconv.ParseInt(segments[3], 10, 32)
if err != nil {
return "", "", 0, err
}
port = uint32(p)
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
default:
return "", "", 0, errors.Errorf("service tag in unexpected format")
}

name, namespace := segments[0], segments[1]
return name, namespace, port, nil
}
8 changes: 8 additions & 0 deletions pkg/plugins/policies/core/rules/common/resourcereader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package common

import core_model "github.com/kumahq/kuma/pkg/core/resources/model"

type ResourceReader interface {
Get(resourceType core_model.ResourceType, ri core_model.ResourceIdentifier) core_model.Resource
ListOrEmpty(resourceType core_model.ResourceType) core_model.ResourceList
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package rules
package merge

import (
"encoding/json"
Expand All @@ -10,16 +10,17 @@ import (
"github.com/pkg/errors"

core_model "github.com/kumahq/kuma/pkg/core/resources/model"
"github.com/kumahq/kuma/pkg/plugins/policies/core/rules/common"
"github.com/kumahq/kuma/pkg/util/pointer"
)

// indicates that all slices that start with this prefix will be appended, not replaced
const appendSlicesPrefix = "Append"

// MergeConfs returns list of confs that may be apply to separate sets of refs.
// Confs returns list of confs that may be apply to separate sets of refs.
// In the usual case it has a single element but for MeshHTTPRoute it is keyed
// by hostname.
func MergeConfs(confs []interface{}) ([]interface{}, error) {
func Confs(confs []interface{}) ([]interface{}, error) {
if len(confs) == 0 {
return nil, nil
}
Expand Down Expand Up @@ -261,7 +262,7 @@ func mergeByKey(vals reflect.Value) (reflect.Value, error) {
if confs.Skip {
continue
}
merged, err := MergeConfs(confs.Defaults)
merged, err := Confs(confs.Defaults)
if err != nil {
return reflect.Value{}, err
}
Expand Down Expand Up @@ -383,3 +384,11 @@ func mustUnwrapStruct(val reflect.Value) reflect.Value {
}
return resVal
}

func Entries[B common.BaseEntry, T common.Entry[B]](items []T) ([]interface{}, error) {
var confs []interface{}
for _, item := range items {
confs = append(confs, item.GetEntry().GetDefault())
}
return Confs(confs)
}
Loading

0 comments on commit 820494c

Please sign in to comment.