Skip to content

Commit a952d56

Browse files
committed
fix: Refactor HTTPRoutePolicy status updates and test logic
Consolidate HTTPRoutePolicy status update functions by adding context.Context parameter and improving ancestor reference handling. Refactor e2e tests to use new helper functions for verifying HTTPRoutePolicy conditions, enhancing test reliability and readability while maintaining existing functionality.
1 parent 7f80427 commit a952d56

File tree

5 files changed

+142
-57
lines changed

5 files changed

+142
-57
lines changed

internal/controller/httproute_controller.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"fmt"
1818
"strings"
1919

20-
"github.com/api7/gopkg/pkg/log"
2120
"github.com/go-logr/logr"
2221
"github.com/pkg/errors"
2322
"go.uber.org/zap"
@@ -39,6 +38,8 @@ import (
3938
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
4039
"sigs.k8s.io/gateway-api/apis/v1alpha2"
4140

41+
"github.com/api7/gopkg/pkg/log"
42+
4243
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
4344
"github.com/apache/apisix-ingress-controller/internal/controller/indexer"
4445
"github.com/apache/apisix-ingress-controller/internal/provider"
@@ -114,7 +115,7 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
114115
hr := new(gatewayv1.HTTPRoute)
115116
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
116117
if client.IgnoreNotFound(err) == nil {
117-
if err := r.updateHTTPRoutePolicyStatusOnDeleting(req.NamespacedName); err != nil {
118+
if err := r.updateHTTPRoutePolicyStatusOnDeleting(ctx, req.NamespacedName); err != nil {
118119
return ctrl.Result{}, err
119120
}
120121
hr.Namespace = req.Namespace

internal/controller/httproutepolicy.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (r *HTTPRouteReconciler) processHTTPRoutePolicies(tctx *provider.TranslateC
7878
return nil
7979
}
8080

81-
func (r *HTTPRouteReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.NamespacedName) error {
81+
func (r *HTTPRouteReconciler) updateHTTPRoutePolicyStatusOnDeleting(ctx context.Context, nn types.NamespacedName) error {
8282
var (
8383
list v1alpha1.HTTPRoutePolicyList
8484
key = indexer.GenIndexKeyWithGK(gatewayv1.GroupName, "HTTPRoute", nn.Namespace, nn.Name)
@@ -104,7 +104,7 @@ func (r *HTTPRouteReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.Nam
104104
parentRefs = append(parentRefs, httpRoute.Spec.ParentRefs...)
105105
}
106106
// delete AncestorRef which is not exist in the all parentRefs for each policy
107-
updateDeleteAncestors(r.Client, r.Log, policy, parentRefs)
107+
updateDeleteAncestors(ctx, r.Client, r.Log, policy, parentRefs)
108108
}
109109

110110
return nil
@@ -145,12 +145,12 @@ func (r *IngressReconciler) processHTTPRoutePolicies(tctx *provider.TranslateCon
145145
return nil
146146
}
147147

148-
func (r *IngressReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.NamespacedName) error {
148+
func (r *IngressReconciler) updateHTTPRoutePolicyStatusOnDeleting(ctx context.Context, nn types.NamespacedName) error {
149149
var (
150150
list v1alpha1.HTTPRoutePolicyList
151151
key = indexer.GenIndexKeyWithGK(networkingv1.GroupName, "Ingress", nn.Namespace, nn.Name)
152152
)
153-
if err := r.List(context.Background(), &list, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
153+
if err := r.List(ctx, &list, client.MatchingFields{indexer.PolicyTargetRefs: key}); err != nil {
154154
return err
155155
}
156156
var (
@@ -164,7 +164,7 @@ func (r *IngressReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.Names
164164
parentRef, ok := ingress2ParentRef[namespacedName]
165165
if !ok {
166166
var ingress networkingv1.Ingress
167-
if err := r.Get(context.Background(), namespacedName, &ingress); err != nil {
167+
if err := r.Get(ctx, namespacedName, &ingress); err != nil {
168168
continue
169169
}
170170
ingressClass, err := r.getIngressClass(&ingress)
@@ -173,15 +173,15 @@ func (r *IngressReconciler) updateHTTPRoutePolicyStatusOnDeleting(nn types.Names
173173
}
174174
parentRef = gatewayv1.ParentReference{
175175
Group: ptr.To(gatewayv1.Group(ingressClass.GroupVersionKind().Group)),
176-
Kind: ptr.To(gatewayv1.Kind("IngressClass")),
176+
Kind: ptr.To(gatewayv1.Kind(KindIngressClass)),
177177
Name: gatewayv1.ObjectName(ingressClass.Name),
178178
}
179179
ingress2ParentRef[namespacedName] = parentRef
180180
}
181181
parentRefs = append(parentRefs, parentRef)
182182
}
183183
// delete AncestorRef which is not exist in the all parentRefs
184-
updateDeleteAncestors(r.Client, r.Log, policy, parentRefs)
184+
updateDeleteAncestors(ctx, r.Client, r.Log, policy, parentRefs)
185185
}
186186

187187
return nil
@@ -234,15 +234,15 @@ func findPoliciesWhichTargetRefTheRule(ruleName *gatewayv1.SectionName, kind str
234234
}
235235

236236
// updateDeleteAncestors removes ancestor references from HTTPRoutePolicy statuses that are no longer present in the provided parentRefs.
237-
func updateDeleteAncestors(client client.Client, logger logr.Logger, policy v1alpha1.HTTPRoutePolicy, parentRefs []gatewayv1.ParentReference) {
237+
func updateDeleteAncestors(ctx context.Context, client client.Client, logger logr.Logger, policy v1alpha1.HTTPRoutePolicy, parentRefs []gatewayv1.ParentReference) {
238238
length := len(policy.Status.Ancestors)
239-
policy.Status.Ancestors = slices.DeleteFunc(policy.Status.Ancestors, func(status v1alpha2.PolicyAncestorStatus) bool {
239+
policy.Status.Ancestors = slices.DeleteFunc(policy.Status.Ancestors, func(ancestor v1alpha2.PolicyAncestorStatus) bool {
240240
return !slices.ContainsFunc(parentRefs, func(ref gatewayv1.ParentReference) bool {
241-
return parentRefValueEqual(status.AncestorRef, ref)
241+
return parentRefValueEqual(ancestor.AncestorRef, ref)
242242
})
243243
})
244244
if length != len(policy.Status.Ancestors) {
245-
if err := client.Status().Update(context.Background(), &policy); err != nil {
245+
if err := client.Status().Update(ctx, &policy); err != nil {
246246
logger.Error(err, "failed to update HTTPRoutePolicy status")
247247
}
248248
}

internal/controller/ingress_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
112112
ingress := new(networkingv1.Ingress)
113113
if err := r.Get(ctx, req.NamespacedName, ingress); err != nil {
114114
if client.IgnoreNotFound(err) == nil {
115-
if err := r.updateHTTPRoutePolicyStatusOnDeleting(req.NamespacedName); err != nil {
115+
if err := r.updateHTTPRoutePolicyStatusOnDeleting(ctx, req.NamespacedName); err != nil {
116116
return ctrl.Result{}, err
117117
}
118118

test/e2e/framework/utils.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,48 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"io"
23+
"log"
2324
"net/http"
2425
"net/url"
2526
"strings"
2627
"sync"
2728
"time"
2829

30+
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
2931
"github.com/gavv/httpexpect/v2"
32+
"github.com/gruntwork-io/terratest/modules/testing"
3033
"github.com/onsi/gomega"
34+
"github.com/pkg/errors"
35+
"github.com/stretchr/testify/require"
3136
"golang.org/x/net/html"
3237
corev1 "k8s.io/api/core/v1"
3338
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39+
"k8s.io/apimachinery/pkg/runtime"
40+
"k8s.io/apimachinery/pkg/types"
41+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
42+
"k8s.io/apimachinery/pkg/util/wait"
3443
"k8s.io/client-go/kubernetes/scheme"
3544
"k8s.io/client-go/tools/remotecommand"
3645
"k8s.io/utils/ptr"
46+
"sigs.k8s.io/controller-runtime/pkg/client"
47+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
48+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
3749
)
3850

51+
var (
52+
scheme_ = runtime.NewScheme()
53+
)
54+
55+
func init() {
56+
utilruntime.Must(scheme.AddToScheme(scheme_))
57+
if err := gatewayv1.Install(scheme_); err != nil {
58+
panic(err)
59+
}
60+
if err := v1alpha1.AddToScheme(scheme_); err != nil {
61+
panic(err)
62+
}
63+
}
64+
3965
func (f *Framework) NewExpectResponse(httpBody any) *httpexpect.Response {
4066
body, err := json.Marshal(httpBody)
4167
f.GomegaT.Expect(err).ShouldNot(gomega.HaveOccurred())
@@ -369,6 +395,42 @@ func CreateTestZipFile(sourceCode, metadata string) ([]byte, error) {
369395
return zipBuffer.Bytes(), nil
370396
}
371397

398+
func HTTPRoutePolicyMustHaveCondition(t testing.TestingT, client client.Client, timeout time.Duration, refNN, hrpNN types.NamespacedName, condition metav1.Condition) {
399+
_ = v1alpha1.AddToScheme(client.Scheme())
400+
401+
err := EventullyHTTPRoutePolicyHaveStatus(client, timeout, hrpNN, func(httpRoutePolicy v1alpha1.HTTPRoutePolicy, status v1alpha1.PolicyStatus) bool {
402+
var (
403+
ancestors = status.Ancestors
404+
conditionFound bool
405+
)
406+
for _, ancestor := range ancestors {
407+
if err := kubernetes.ConditionsHaveLatestObservedGeneration(&httpRoutePolicy, ancestor.Conditions); err != nil {
408+
log.Printf("HTTPRoutePolicy %s (parentRef=%v) %v", hrpNN, parentRefToString(ancestor.AncestorRef), err)
409+
return false
410+
}
411+
412+
if ancestor.AncestorRef.Name == gatewayv1.ObjectName(refNN.Name) && (ancestor.AncestorRef.Namespace == nil || string(*ancestor.AncestorRef.Namespace) == refNN.Namespace) {
413+
if findConditionInList(t, ancestor.Conditions, condition) {
414+
conditionFound = true
415+
}
416+
}
417+
}
418+
return conditionFound
419+
})
420+
421+
require.NoError(t, err, "error waiting for HTTPRoutePolicy status to have a Condition matching expectations")
422+
}
423+
424+
func EventullyHTTPRoutePolicyHaveStatus(client client.Client, timeout time.Duration, hrpNN types.NamespacedName, f func(httpRoutePolicy v1alpha1.HTTPRoutePolicy, status v1alpha1.PolicyStatus) bool) error {
425+
return wait.PollUntilContextTimeout(context.Background(), time.Second, timeout, true, func(ctx context.Context) (done bool, err error) {
426+
var httpRoutePolicy v1alpha1.HTTPRoutePolicy
427+
if err = client.Get(ctx, hrpNN, &httpRoutePolicy); err != nil {
428+
return false, errors.Errorf("error fetching HTTPRoutePolicy %v: %v", hrpNN, err)
429+
}
430+
return f(httpRoutePolicy, httpRoutePolicy.Status), nil
431+
})
432+
}
433+
372434
func addFileToZip(zipWriter *zip.Writer, fileName, fileContent string) error {
373435
file, err := zipWriter.Create(fileName)
374436
if err != nil {
@@ -378,3 +440,30 @@ func addFileToZip(zipWriter *zip.Writer, fileName, fileContent string) error {
378440
_, err = file.Write([]byte(fileContent))
379441
return err
380442
}
443+
444+
func parentRefToString(p gatewayv1.ParentReference) string {
445+
if p.Namespace != nil && *p.Namespace != "" {
446+
return fmt.Sprintf("%v/%v", p.Namespace, p.Name)
447+
}
448+
return string(p.Name)
449+
}
450+
451+
func findConditionInList(t testing.TestingT, conditions []metav1.Condition, expected metav1.Condition) bool {
452+
for _, cond := range conditions {
453+
if cond.Type == expected.Type {
454+
// an empty Status string means "Match any status".
455+
if expected.Status == "" || cond.Status == expected.Status {
456+
// an empty Reason string means "Match any reason".
457+
if expected.Reason == "" || cond.Reason == expected.Reason {
458+
return true
459+
}
460+
log.Printf("%s condition Reason set to %s, expected %s", expected.Type, cond.Reason, expected.Reason)
461+
}
462+
463+
log.Printf("%s condition set to Status %s with Reason %v, expected Status %s", expected.Type, cond.Status, cond.Reason, expected.Status)
464+
}
465+
}
466+
467+
log.Printf("%s was not in conditions list [%v]", expected.Type, conditions)
468+
return false
469+
}

test/e2e/ingress/ingress.go

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ import (
2020
"strings"
2121
"time"
2222

23+
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
24+
"github.com/apache/apisix-ingress-controller/test/e2e/framework"
25+
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
2326
. "github.com/onsi/ginkgo/v2"
2427
. "github.com/onsi/gomega"
2528
"github.com/stretchr/testify/assert"
26-
27-
"github.com/apache/apisix-ingress-controller/test/e2e/framework"
28-
"github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/types"
31+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
2932
)
3033

3134
const _secretName = "test-ingress-tls"
@@ -723,46 +726,38 @@ spec:
723726
})
724727

725728
// todo: unstable test case, pending for now
726-
PIt("HTTPRoutePolicy status changes on Ingress deleting", func() {
727-
By("create Ingress")
728-
err := s.CreateResourceFromString(ingressSpec)
729-
Expect(err).NotTo(HaveOccurred(), "creating Ingress")
730-
731-
By("create HTTPRoutePolicy")
732-
err = s.CreateResourceFromString(httpRoutePolicySpec0)
733-
Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy")
734-
Eventually(func() string {
735-
spec, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-0")
736-
Expect(err).NotTo(HaveOccurred(), "HTTPRoutePolicy status should be True")
737-
return spec
738-
}).
739-
WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring(`status: "True"`))
740-
741-
By("request the route without vars should be Not Found")
742-
Eventually(func() int {
743-
return s.NewAPISIXClient().GET("/get").WithHost("example.com").Expect().Raw().StatusCode
744-
}).
745-
WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound))
746-
747-
By("request the route with the correct vars should be OK")
748-
s.NewAPISIXClient().GET("/get").WithHost("example.com").
749-
WithHeader("X-HRP-Name", "http-route-policy-0").Expect().Status(http.StatusOK)
750-
751-
By("delete ingress")
752-
err = s.DeleteResource("Ingress", "default")
753-
Expect(err).NotTo(HaveOccurred(), "delete Ingress")
754-
Eventually(func() int {
755-
return s.NewAPISIXClient().GET("/get").WithHost("example.com").
756-
WithHeader("X-HRP-Name", "http-route-policy-0").Expect().Raw().StatusCode
757-
}).
758-
WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound))
759-
760-
Eventually(func() string {
761-
spec, err := s.GetResourceYaml("HTTPRoutePolicy", "http-route-policy-0")
762-
Expect(err).NotTo(HaveOccurred(), "getting HTTPRoutePolicy")
763-
return spec
764-
}).WithTimeout(8 * time.Second).ProbeEvery(time.Second).ShouldNot(ContainSubstring("ancestorRef:"))
765-
})
729+
for i := 0; i < 100; i++ {
730+
It("HTTPRoutePolicy status changes on Ingress deleting", func() {
731+
By("create Ingress")
732+
err := s.CreateResourceFromString(ingressSpec)
733+
Expect(err).NotTo(HaveOccurred(), "creating Ingress")
734+
735+
By("create HTTPRoutePolicy")
736+
err = s.CreateResourceFromString(httpRoutePolicySpec0)
737+
Expect(err).NotTo(HaveOccurred(), "creating HTTPRoutePolicy")
738+
framework.HTTPRoutePolicyMustHaveCondition(s.GinkgoT, s.K8sClient, 8*time.Second,
739+
types.NamespacedName{Namespace: s.Namespace(), Name: "apisix"},
740+
types.NamespacedName{Namespace: s.Namespace(), Name: "http-route-policy-0"},
741+
metav1.Condition{
742+
Type: string(gatewayv1alpha2.PolicyConditionAccepted),
743+
Status: metav1.ConditionTrue,
744+
Reason: string(gatewayv1alpha2.PolicyReasonAccepted),
745+
},
746+
)
747+
748+
By("delete ingress")
749+
err = s.DeleteResource("Ingress", "default")
750+
Expect(err).NotTo(HaveOccurred(), "delete Ingress")
751+
752+
err = framework.EventullyHTTPRoutePolicyHaveStatus(s.K8sClient, 8*time.Second,
753+
types.NamespacedName{Namespace: s.Namespace(), Name: "http-route-policy-0"},
754+
func(_ v1alpha1.HTTPRoutePolicy, status v1alpha1.PolicyStatus) bool {
755+
return len(status.Ancestors) == 0
756+
},
757+
)
758+
Expect(err).NotTo(HaveOccurred(), "expected HTTPRoutePolicy.Status has no Ancestor")
759+
})
760+
}
766761
})
767762

768763
Context("Ingress with GatewayProxy Update", func() {

0 commit comments

Comments
 (0)