Skip to content

Commit

Permalink
sotw: rlp workflow
Browse files Browse the repository at this point in the history
Signed-off-by: Guilherme Cassolato <guicassolato@gmail.com>
  • Loading branch information
guicassolato committed Oct 7, 2024
1 parent c194aa4 commit 6436530
Show file tree
Hide file tree
Showing 42 changed files with 1,575 additions and 2,353 deletions.
220 changes: 220 additions & 0 deletions api/v1/merge_strategies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
"strings"

"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"

"github.com/kuadrant/policy-machinery/machinery"
)

const (
AtomicMergeStrategy = "atomic"
PolicyRuleMergeStrategy = "merge"
)

type MergeableRule struct {
Spec any
Source string
}

// +kubebuilder:object:generate=false
type MergeablePolicy interface {
machinery.Policy

Rules() map[string]MergeableRule
SetRules(map[string]MergeableRule)
Empty() bool

DeepCopyObject() runtime.Object
}

type SortablePolicy interface {
machinery.Policy
GetCreationTimestamp() metav1.Time
}

type PolicyByCreationTimestamp []SortablePolicy

func (a PolicyByCreationTimestamp) Len() int { return len(a) }
func (a PolicyByCreationTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a PolicyByCreationTimestamp) Less(i, j int) bool {
p1Time := ptr.To(a[i].GetCreationTimestamp())
p2Time := ptr.To(a[j].GetCreationTimestamp())
if !p1Time.Equal(p2Time) {
return p1Time.Before(p2Time)

Check warning on line 68 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L62-L68

Added lines #L62 - L68 were not covered by tests
}
// The object appearing first in alphabetical order by "{namespace}/{name}".
return fmt.Sprintf("%s/%s", a[i].GetNamespace(), a[i].GetName()) < fmt.Sprintf("%s/%s", a[j].GetNamespace(), a[j].GetName())

Check warning on line 71 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L71

Added line #L71 was not covered by tests
}

// AtomicDefaultsMergeStrategy implements a merge strategy that returns the target Policy if it exists,
// otherwise it returns the source Policy.
func AtomicDefaultsMergeStrategy(source, target machinery.Policy) machinery.Policy {
if source == nil {
return target

Check warning on line 78 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L76-L78

Added lines #L76 - L78 were not covered by tests
}
if target == nil {
return source

Check warning on line 81 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L80-L81

Added lines #L80 - L81 were not covered by tests
}

mergeableTargetPolicy := target.(MergeablePolicy)

Check warning on line 84 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L84

Added line #L84 was not covered by tests

if !mergeableTargetPolicy.Empty() {
return mergeableTargetPolicy.DeepCopyObject().(machinery.Policy)

Check warning on line 87 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L86-L87

Added lines #L86 - L87 were not covered by tests
}

return source.(MergeablePolicy).DeepCopyObject().(machinery.Policy)

Check warning on line 90 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L90

Added line #L90 was not covered by tests
}

var _ machinery.MergeStrategy = AtomicDefaultsMergeStrategy

// AtomicOverridesMergeStrategy implements a merge strategy that overrides a target Policy with
// a source one.
func AtomicOverridesMergeStrategy(source, _ machinery.Policy) machinery.Policy {
if source == nil {
return nil

Check warning on line 99 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L97-L99

Added lines #L97 - L99 were not covered by tests
}
return source.(MergeablePolicy).DeepCopyObject().(machinery.Policy)

Check warning on line 101 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L101

Added line #L101 was not covered by tests
}

var _ machinery.MergeStrategy = AtomicOverridesMergeStrategy

// PolicyRuleDefaultsMergeStrategy implements a merge strategy that merges a source Policy into a target one
// by keeping the policy rules from the target and adding the ones from the source that do not exist in the target.
func PolicyRuleDefaultsMergeStrategy(source, target machinery.Policy) machinery.Policy {
if source == nil {
return target

Check warning on line 110 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L108-L110

Added lines #L108 - L110 were not covered by tests
}
if target == nil {
return source

Check warning on line 113 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L112-L113

Added lines #L112 - L113 were not covered by tests
}

sourceMergeablePolicy := source.(MergeablePolicy)
targetMergeablePolicy := target.(MergeablePolicy)

Check warning on line 117 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L116-L117

Added lines #L116 - L117 were not covered by tests

// copy rules from the target
rules := targetMergeablePolicy.Rules()

Check warning on line 120 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L120

Added line #L120 was not covered by tests

// add extra rules from the source
for ruleID, rule := range sourceMergeablePolicy.Rules() {
if _, ok := targetMergeablePolicy.Rules()[ruleID]; !ok {
rules[ruleID] = MergeableRule{
Spec: rule.Spec,
Source: source.GetLocator(),

Check warning on line 127 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L123-L127

Added lines #L123 - L127 were not covered by tests
}
}
}

mergedPolicy := targetMergeablePolicy.DeepCopyObject().(MergeablePolicy)
mergedPolicy.SetRules(rules)
return mergedPolicy

Check warning on line 134 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L132-L134

Added lines #L132 - L134 were not covered by tests
}

var _ machinery.MergeStrategy = PolicyRuleDefaultsMergeStrategy

// PolicyRuleOverridesMergeStrategy implements a merge strategy that merges a source Policy into a target one
// by using the policy rules from the source and keeping from the target only the policy rules that do not exist in
// the source.
func PolicyRuleOverridesMergeStrategy(source, target machinery.Policy) machinery.Policy {
sourceMergeablePolicy := source.(MergeablePolicy)
targetMergeablePolicy := target.(MergeablePolicy)

Check warning on line 144 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L142-L144

Added lines #L142 - L144 were not covered by tests

// copy rules from the source
rules := sourceMergeablePolicy.Rules()

Check warning on line 147 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L147

Added line #L147 was not covered by tests

// add extra rules from the target
for ruleID, rule := range targetMergeablePolicy.Rules() {
if _, ok := sourceMergeablePolicy.Rules()[ruleID]; !ok {
rules[ruleID] = rule

Check warning on line 152 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L150-L152

Added lines #L150 - L152 were not covered by tests
}
}

mergedPolicy := targetMergeablePolicy.DeepCopyObject().(MergeablePolicy)
mergedPolicy.SetRules(rules)
return mergedPolicy

Check warning on line 158 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L156-L158

Added lines #L156 - L158 were not covered by tests
}

var _ machinery.MergeStrategy = PolicyRuleOverridesMergeStrategy

func DefaultsMergeStrategy(strategy string) machinery.MergeStrategy {
switch strategy {
case AtomicMergeStrategy:
return AtomicDefaultsMergeStrategy
case PolicyRuleMergeStrategy:
return PolicyRuleDefaultsMergeStrategy
default:
return AtomicDefaultsMergeStrategy

Check warning on line 170 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L163-L170

Added lines #L163 - L170 were not covered by tests
}
}

func OverridesMergeStrategy(strategy string) machinery.MergeStrategy {
switch strategy {
case AtomicMergeStrategy:
return AtomicOverridesMergeStrategy
case PolicyRuleMergeStrategy:
return PolicyRuleOverridesMergeStrategy
default:
return AtomicOverridesMergeStrategy

Check warning on line 181 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L174-L181

Added lines #L174 - L181 were not covered by tests
}
}

// EffectivePolicyForPath returns the effective policy for a given path, merging all policies in the path.
// The policies in the path are sorted from the least specific to the most specific.
// Only policies whose predicate returns true are considered.
func EffectivePolicyForPath[T machinery.Policy](path []machinery.Targetable, predicate func(machinery.Policy) bool) *T {
policies := PoliciesInPath(path, predicate)
if len(policies) == 0 {
return nil

Check warning on line 191 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L188-L191

Added lines #L188 - L191 were not covered by tests
}

// map reduces the policies from most specific to least specific, merging them into one effective policy
effectivePolicy := lo.ReduceRight(policies, func(effectivePolicy machinery.Policy, policy machinery.Policy, _ int) machinery.Policy {
return effectivePolicy.Merge(policy)
}, policies[len(policies)-1])

Check warning on line 197 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L195-L197

Added lines #L195 - L197 were not covered by tests

concreteEffectivePolicy, _ := effectivePolicy.(T)
return &concreteEffectivePolicy

Check warning on line 200 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L199-L200

Added lines #L199 - L200 were not covered by tests
}

// OrderedPoliciesForPath gathers all policies in a path sorted from the least specific to the most specific.
// Only policies whose predicate returns true are considered.
func PoliciesInPath(path []machinery.Targetable, predicate func(machinery.Policy) bool) []machinery.Policy {
return lo.FlatMap(path, func(targetable machinery.Targetable, _ int) []machinery.Policy {
policies := lo.FilterMap(targetable.Policies(), func(policy machinery.Policy, _ int) (SortablePolicy, bool) {
p, sortable := policy.(SortablePolicy)
return p, sortable && predicate(p)
})
sort.Sort(PolicyByCreationTimestamp(policies))
return lo.Map(policies, func(policy SortablePolicy, _ int) machinery.Policy { return policy })

Check warning on line 212 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L205-L212

Added lines #L205 - L212 were not covered by tests
})
}

func PathID(path []machinery.Targetable) string {
s := strings.Join(lo.Map(path, machinery.MapTargetableToLocatorFunc), ">")
hash := sha256.Sum256([]byte(s))
return hex.EncodeToString(hash[:8])

Check warning on line 219 in api/v1/merge_strategies.go

View check run for this annotation

Codecov / codecov/patch

api/v1/merge_strategies.go#L216-L219

Added lines #L216 - L219 were not covered by tests
}
32 changes: 21 additions & 11 deletions api/v1beta3/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2021.
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -14,23 +14,33 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package v1beta3 contains API Schema definitions for the kuadrant v1beta3 API group
// API schema definitions for the Kuadrant v1beta3 API group
// +kubebuilder:object:generate=true
// +groupName=kuadrant.io
package v1beta3

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
ctrl "sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "kuadrant.io", Version: "v1beta3"}
// GroupName specifies the group name used to register the objects.
const GroupName = "kuadrant.io"

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// GroupVersion specifies the group and the version used to register the objects.
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta3"}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
// SchemeGroupVersion is group version used to register these objects
// Deprecated: use GroupVersion instead.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta3"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
var SchemeBuilder = &ctrl.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
var AddToScheme = SchemeBuilder.AddToScheme

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()

Check warning on line 45 in api/v1beta3/groupversion_info.go

View check run for this annotation

Codecov / codecov/patch

api/v1beta3/groupversion_info.go#L44-L45

Added lines #L44 - L45 were not covered by tests
}
Loading

0 comments on commit 6436530

Please sign in to comment.