Skip to content

Commit 90aff0b

Browse files
authored
chore: httproutepolicy status on targetref deleting (#111)
1 parent c891c8b commit 90aff0b

File tree

9 files changed

+233
-151
lines changed

9 files changed

+233
-151
lines changed

internal/controller/httproute_controller.go

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77

88
"github.com/go-logr/logr"
99
"github.com/pkg/errors"
10+
"golang.org/x/exp/slices"
1011
corev1 "k8s.io/api/core/v1"
1112
discoveryv1 "k8s.io/api/discovery/v1"
1213
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314
"k8s.io/apimachinery/pkg/runtime"
1415
"k8s.io/apimachinery/pkg/types"
16+
"k8s.io/utils/ptr"
1517
ctrl "sigs.k8s.io/controller-runtime"
1618
"sigs.k8s.io/controller-runtime/pkg/builder"
1719
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -98,6 +100,9 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
98100
hr := new(gatewayv1.HTTPRoute)
99101
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
100102
if client.IgnoreNotFound(err) == nil {
103+
if err := r.updateHTTPRoutePolicyStatusOnDeleting(req.NamespacedName); err != nil {
104+
return ctrl.Result{}, err
105+
}
101106
hr.Namespace = req.Namespace
102107
hr.Name = req.Name
103108

@@ -514,21 +519,14 @@ func httpRoutePolicyPredicateFuncs(channel chan event.GenericEvent) predicate.Pr
514519
if !ok0 || !ok1 {
515520
return false
516521
}
517-
var discardsRefs = make(map[string]v1alpha2.LocalPolicyTargetReferenceWithSectionName)
518-
for _, ref := range oldPolicy.Spec.TargetRefs {
519-
key := indexer.GenHTTPRoutePolicyIndexKey(string(ref.Group), string(ref.Kind), e.ObjectOld.GetNamespace(), string(ref.Name), "")
520-
discardsRefs[key] = ref
521-
}
522-
for _, ref := range newPolicy.Spec.TargetRefs {
523-
key := indexer.GenHTTPRoutePolicyIndexKey(string(ref.Group), string(ref.Kind), e.ObjectOld.GetNamespace(), string(ref.Name), "")
524-
delete(discardsRefs, key)
525-
}
522+
discardsRefs := slices.DeleteFunc(oldPolicy.Spec.TargetRefs, func(oldRef v1alpha2.LocalPolicyTargetReferenceWithSectionName) bool {
523+
return slices.ContainsFunc(newPolicy.Spec.TargetRefs, func(newRef v1alpha2.LocalPolicyTargetReferenceWithSectionName) bool {
524+
return oldRef.LocalPolicyTargetReference == newRef.LocalPolicyTargetReference && ptr.Equal(oldRef.SectionName, newRef.SectionName)
525+
})
526+
})
526527
if len(discardsRefs) > 0 {
527528
dump := oldPolicy.DeepCopy()
528-
dump.Spec.TargetRefs = make([]v1alpha2.LocalPolicyTargetReferenceWithSectionName, 0, len(discardsRefs))
529-
for _, ref := range discardsRefs {
530-
dump.Spec.TargetRefs = append(dump.Spec.TargetRefs, ref)
531-
}
529+
dump.Spec.TargetRefs = discardsRefs
532530
channel <- event.GenericEvent{Object: dump}
533531
}
534532
return true

internal/controller/httproutepolicy.go

Lines changed: 148 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package controller
22

33
import (
44
"context"
5-
"time"
5+
"slices"
66

7+
"github.com/go-logr/logr"
78
networkingv1 "k8s.io/api/networking/v1"
89
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/types"
911
"k8s.io/utils/ptr"
1012
"sigs.k8s.io/controller-runtime/pkg/client"
1113
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -19,115 +21,107 @@ import (
1921
func (r *HTTPRouteReconciler) processHTTPRoutePolicies(tctx *provider.TranslateContext, httpRoute *gatewayv1.HTTPRoute) error {
2022
// list HTTPRoutePolices which sectionName is not specified
2123
var (
22-
checker = conflictChecker{
23-
object: httpRoute,
24-
policies: make(map[targetRefKey][]v1alpha1.HTTPRoutePolicy),
25-
}
26-
listForAllRules v1alpha1.HTTPRoutePolicyList
27-
key = indexer.GenHTTPRoutePolicyIndexKey(gatewayv1.GroupName, "HTTPRoute", httpRoute.GetNamespace(), httpRoute.GetName(), "")
24+
list v1alpha1.HTTPRoutePolicyList
25+
key = indexer.GenIndexKeyWithGK(gatewayv1.GroupName, "HTTPRoute", httpRoute.GetNamespace(), httpRoute.GetName())
2826
)
29-
if err := r.List(context.Background(), &listForAllRules, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
27+
if err := r.List(context.Background(), &list, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
3028
return err
3129
}
3230

33-
for _, item := range listForAllRules.Items {
34-
checker.append("", item)
35-
tctx.HTTPRoutePolicies["*"] = append(tctx.HTTPRoutePolicies["*"], item)
31+
if len(list.Items) == 0 {
32+
return nil
3633
}
3734

35+
var conflicts = make(map[types.NamespacedName]v1alpha1.HTTPRoutePolicy)
3836
for _, rule := range httpRoute.Spec.Rules {
39-
if rule.Name == nil {
40-
continue
37+
var policies = findPoliciesWhichTargetRefTheRule(rule.Name, "HTTPRoute", list)
38+
if conflict := checkPoliciesConflict(policies); conflict {
39+
for _, policy := range policies {
40+
namespacedName := types.NamespacedName{Namespace: policy.GetNamespace(), Name: policy.GetName()}
41+
conflicts[namespacedName] = policy
42+
}
4143
}
44+
}
4245

46+
for i := range list.Items {
4347
var (
44-
ruleName = string(*rule.Name)
45-
listForSectionRules v1alpha1.HTTPRoutePolicyList
46-
key = indexer.GenHTTPRoutePolicyIndexKey(gatewayv1.GroupName, "HTTPRoute", httpRoute.GetNamespace(), httpRoute.GetName(), ruleName)
48+
policy = list.Items[i]
49+
namespacedName = types.NamespacedName{Namespace: policy.GetNamespace(), Name: policy.GetName()}
50+
51+
status = false
52+
reason = string(v1alpha2.PolicyReasonConflicted)
53+
message = "HTTPRoutePolicy conflict with others target to the HTTPRoute"
4754
)
48-
if err := r.List(context.Background(), &listForSectionRules, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
49-
continue
50-
}
51-
for _, item := range listForSectionRules.Items {
52-
checker.append(ruleName, item)
53-
tctx.HTTPRoutePolicies[ruleName] = append(tctx.HTTPRoutePolicies[ruleName], item)
55+
if _, conflict := conflicts[namespacedName]; !conflict {
56+
status = true
57+
reason = string(v1alpha2.PolicyReasonAccepted)
58+
message = ""
59+
60+
tctx.HTTPRoutePolicies = append(tctx.HTTPRoutePolicies, policy)
5461
}
62+
modifyHTTPRoutePolicyStatus(httpRoute.Spec.ParentRefs, &policy, status, reason, message)
63+
tctx.StatusUpdaters = append(tctx.StatusUpdaters, &policy)
5564
}
5665

57-
// todo: unreachable
58-
// if the HTTPRoute is deleted, clear tctx.HTTPRoutePolicies and delete Ancestors from HTTPRoutePolicies status
59-
// if !httpRoute.GetDeletionTimestamp().IsZero() {
60-
// for _, policies := range checker.policies {
61-
// for i := range policies {
62-
// policy := policies[i]
63-
// _ = DeleteAncestors(&policy.Status, httpRoute.Spec.ParentRefs)
64-
// data, _ := json.Marshal(policy.Status)
65-
// r.Log.Info("policy status after delete ancestor", "data", string(data))
66-
// if err := r.Status().Update(context.Background(), &policy); err != nil {
67-
// r.Log.Error(err, "failed to Update policy status")
68-
// }
69-
// // tctx.StatusUpdaters = append(tctx.StatusUpdaters, &policy)
70-
// }
71-
// }
72-
// return nil
73-
// }
66+
return nil
67+
}
7468

69+
func (r *HTTPRouteReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.NamespacedName) error {
7570
var (
76-
status = true
77-
reason = string(v1alpha2.PolicyReasonAccepted)
78-
message string
71+
list v1alpha1.HTTPRoutePolicyList
72+
key = indexer.GenIndexKeyWithGK(gatewayv1.GroupName, "HTTPRoute", nn.Namespace, nn.Name)
7973
)
80-
if checker.conflict {
81-
status = false
82-
reason = string(v1alpha2.PolicyReasonConflicted)
83-
message = "HTTPRoutePolicy conflict with others target to the HTTPRoute"
84-
85-
// clear HTTPRoutePolices from TranslateContext
86-
tctx.HTTPRoutePolicies = make(map[string][]v1alpha1.HTTPRoutePolicy)
74+
if err := r.List(context.Background(), &list, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
75+
return err
8776
}
88-
89-
for _, policies := range checker.policies {
90-
for i := range policies {
91-
policy := policies[i]
92-
modifyHTTPRoutePolicyStatus(httpRoute.Spec.ParentRefs, &policy, status, reason, message)
93-
tctx.StatusUpdaters = append(tctx.StatusUpdaters, &policy)
77+
var (
78+
httpRoutes = make(map[types.NamespacedName]gatewayv1.HTTPRoute)
79+
)
80+
for _, policy := range list.Items {
81+
// collect all parentRefs for the HTTPRoutePolicy
82+
var parentRefs []gatewayv1.ParentReference
83+
for _, ref := range policy.Spec.TargetRefs {
84+
var namespacedName = types.NamespacedName{Namespace: policy.GetNamespace(), Name: string(ref.Name)}
85+
httpRoute, ok := httpRoutes[namespacedName]
86+
if !ok {
87+
if err := r.Get(context.Background(), namespacedName, &httpRoute); err != nil {
88+
continue
89+
}
90+
httpRoutes[namespacedName] = httpRoute
91+
}
92+
parentRefs = append(parentRefs, httpRoute.Spec.ParentRefs...)
9493
}
94+
// delete AncestorRef which is not exist in the all parentRefs for each policy
95+
updateDeleteAncestors(r.Client, r.Log, policy, parentRefs)
9596
}
9697

9798
return nil
9899
}
99100

100101
func (r *IngressReconciler) processHTTPRoutePolicies(tctx *provider.TranslateContext, ingress *networkingv1.Ingress) error {
101102
var (
102-
checker = conflictChecker{
103-
object: ingress,
104-
policies: make(map[targetRefKey][]v1alpha1.HTTPRoutePolicy),
105-
conflict: false,
106-
}
107103
list v1alpha1.HTTPRoutePolicyList
108-
key = indexer.GenHTTPRoutePolicyIndexKey(networkingv1.GroupName, "Ingress", ingress.GetNamespace(), ingress.GetName(), "")
104+
key = indexer.GenIndexKeyWithGK(networkingv1.GroupName, "Ingress", ingress.GetNamespace(), ingress.GetName())
109105
)
110106
if err := r.List(context.Background(), &list, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
111107
return err
112108
}
113109

114-
for _, item := range list.Items {
115-
checker.append("", item)
116-
tctx.HTTPRoutePolicies["*"] = append(tctx.HTTPRoutePolicies["*"], item)
110+
if len(list.Items) == 0 {
111+
return nil
117112
}
118113

119114
var (
120-
status = true
121-
reason = string(v1alpha2.PolicyReasonAccepted)
122-
message string
123-
)
124-
if checker.conflict {
125-
status = false
126-
reason = string(v1alpha2.PolicyReasonConflicted)
115+
status = false
116+
reason = string(v1alpha2.PolicyReasonConflicted)
127117
message = "HTTPRoutePolicy conflict with others target to the Ingress"
118+
)
119+
if conflict := checkPoliciesConflict(list.Items); !conflict {
120+
status = true
121+
reason = string(v1alpha2.PolicyReasonAccepted)
122+
message = ""
128123

129-
// clear HTTPRoutePolicies from TranslateContext
130-
tctx.HTTPRoutePolicies = make(map[string][]v1alpha1.HTTPRoutePolicy)
124+
tctx.HTTPRoutePolicies = list.Items
131125
}
132126

133127
for i := range list.Items {
@@ -139,12 +133,54 @@ func (r *IngressReconciler) processHTTPRoutePolicies(tctx *provider.TranslateCon
139133
return nil
140134
}
141135

136+
func (r *IngressReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.NamespacedName) error {
137+
var (
138+
list v1alpha1.HTTPRoutePolicyList
139+
key = indexer.GenIndexKeyWithGK(networkingv1.GroupName, "Ingress", nn.Namespace, nn.Name)
140+
)
141+
if err := r.List(context.Background(), &list, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
142+
return err
143+
}
144+
var (
145+
ingress2ParentRef = make(map[types.NamespacedName]gatewayv1.ParentReference)
146+
)
147+
for _, policy := range list.Items {
148+
// collect all parentRefs for the HTTPRoutePolicy
149+
var parentRefs []gatewayv1.ParentReference
150+
for _, ref := range policy.Spec.TargetRefs {
151+
var namespacedName = types.NamespacedName{Namespace: policy.GetNamespace(), Name: string(ref.Name)}
152+
parentRef, ok := ingress2ParentRef[namespacedName]
153+
if !ok {
154+
var ingress networkingv1.Ingress
155+
if err := r.Get(context.Background(), namespacedName, &ingress); err != nil {
156+
continue
157+
}
158+
ingressClass, err := r.getIngressClass(&ingress)
159+
if err != nil {
160+
continue
161+
}
162+
parentRef = gatewayv1.ParentReference{
163+
Group: ptr.To(gatewayv1.Group(ingressClass.GroupVersionKind().Group)),
164+
Kind: ptr.To(gatewayv1.Kind("IngressClass")),
165+
Name: gatewayv1.ObjectName(ingressClass.Name),
166+
}
167+
ingress2ParentRef[namespacedName] = parentRef
168+
}
169+
parentRefs = append(parentRefs, parentRef)
170+
}
171+
// delete AncestorRef which is not exist in the all parentRefs
172+
updateDeleteAncestors(r.Client, r.Log, policy, parentRefs)
173+
}
174+
175+
return nil
176+
}
177+
142178
func modifyHTTPRoutePolicyStatus(parentRefs []gatewayv1.ParentReference, policy *v1alpha1.HTTPRoutePolicy, status bool, reason, message string) {
143179
condition := metav1.Condition{
144180
Type: string(v1alpha2.PolicyConditionAccepted),
145181
Status: metav1.ConditionTrue,
146182
ObservedGeneration: policy.GetGeneration(),
147-
LastTransitionTime: metav1.Time{Time: time.Now()},
183+
LastTransitionTime: metav1.Now(),
148184
Reason: reason,
149185
Message: message,
150186
}
@@ -154,37 +190,48 @@ func modifyHTTPRoutePolicyStatus(parentRefs []gatewayv1.ParentReference, policy
154190
_ = SetAncestors(&policy.Status, parentRefs, condition)
155191
}
156192

157-
type conflictChecker struct {
158-
object client.Object
159-
policies map[targetRefKey][]v1alpha1.HTTPRoutePolicy
160-
conflict bool
193+
// checkPoliciesConflict determines if there is a conflict among the given HTTPRoutePolicy objects based on their priority values.
194+
// It returns true if any policy has a different priority than the first policy in the list, otherwise false.
195+
// An empty or single-element policies slice is considered non-conflicting.
196+
// The function assumes all policies have a valid Spec.Priority field for comparison.
197+
func checkPoliciesConflict(policies []v1alpha1.HTTPRoutePolicy) bool {
198+
if len(policies) == 0 {
199+
return false
200+
}
201+
priority := policies[0].Spec.Priority
202+
for _, policy := range policies {
203+
if !ptr.Equal(policy.Spec.Priority, priority) {
204+
return true
205+
}
206+
}
207+
return false
161208
}
162209

163-
type targetRefKey struct {
164-
Group gatewayv1.Group
165-
Namespace gatewayv1.Namespace
166-
Name gatewayv1.ObjectName
167-
SectionName gatewayv1.SectionName
210+
// findPoliciesWhichTargetRefTheRule filters HTTPRoutePolicy objects whose TargetRefs match the given ruleName and kind.
211+
// A match occurs if the TargetRef's Kind equals the provided kind and its SectionName is nil, empty, or equal to ruleName.
212+
func findPoliciesWhichTargetRefTheRule(ruleName *gatewayv1.SectionName, kind string, list v1alpha1.HTTPRoutePolicyList) (policies []v1alpha1.HTTPRoutePolicy) {
213+
for _, policy := range list.Items {
214+
for _, ref := range policy.Spec.TargetRefs {
215+
if string(ref.Kind) == kind && (ref.SectionName == nil || *ref.SectionName == "" || ptr.Equal(ref.SectionName, ruleName)) {
216+
policies = append(policies, policy)
217+
break
218+
}
219+
}
220+
}
221+
return
168222
}
169223

170-
func (c *conflictChecker) append(sectionName string, policy v1alpha1.HTTPRoutePolicy) {
171-
key := targetRefKey{
172-
Group: gatewayv1.GroupName,
173-
Namespace: gatewayv1.Namespace(c.object.GetNamespace()),
174-
Name: gatewayv1.ObjectName(c.object.GetName()),
175-
SectionName: gatewayv1.SectionName(sectionName),
176-
}
177-
c.policies[key] = append(c.policies[key], policy)
178-
179-
if !c.conflict {
180-
Loop:
181-
for _, items := range c.policies {
182-
for _, item := range items {
183-
if !ptr.Equal(item.Spec.Priority, policy.Spec.Priority) {
184-
c.conflict = true
185-
break Loop
186-
}
187-
}
224+
// updateDeleteAncestors removes ancestor references from HTTPRoutePolicy statuses that are no longer present in the provided parentRefs.
225+
func updateDeleteAncestors(client client.Client, logger logr.Logger, policy v1alpha1.HTTPRoutePolicy, parentRefs []gatewayv1.ParentReference) {
226+
length := len(policy.Status.Ancestors)
227+
policy.Status.Ancestors = slices.DeleteFunc(policy.Status.Ancestors, func(status v1alpha2.PolicyAncestorStatus) bool {
228+
return !slices.ContainsFunc(parentRefs, func(ref gatewayv1.ParentReference) bool {
229+
return parentRefValueEqual(status.AncestorRef, ref)
230+
})
231+
})
232+
if length != len(policy.Status.Ancestors) {
233+
if err := client.Status().Update(context.Background(), &policy); err != nil {
234+
logger.Error(err, "failed to update HTTPRoutePolicy status")
188235
}
189236
}
190237
}

0 commit comments

Comments
 (0)