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

refactor(rules): organize code in the packages, prepare for inbound rules #12559

Open
wants to merge 7 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
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
Loading