Skip to content

Commit ba5c304

Browse files
authored
feat: Add support for HTTPRoutePolicy in HTTPRoute reconciliation (#93)
1 parent d7fbe44 commit ba5c304

File tree

14 files changed

+646
-60
lines changed

14 files changed

+646
-60
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ KIND_NAME ?= api7-ingress-cluster
1313
GATEAY_API_VERSION ?= v1.2.0
1414

1515
DASHBOARD_VERSION ?= dev
16-
TEST_TIMEOUT ?= 30m
16+
TEST_TIMEOUT ?= 45m
1717

1818
export KUBECONFIG = /tmp/$(KIND_NAME).kubeconfig
1919

api/v1alpha1/httproutepolicy_types.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ type HTTPRoutePolicySpec struct {
2727
// TargetRef identifies an API object (enum: HTTPRoute, Ingress) to apply HTTPRoutePolicy to.
2828
//
2929
// target references.
30-
// +listType=map
31-
// +listMapKey=group
32-
// +listMapKey=kind
33-
// +listMapKey=name
3430
// +kubebuilder:validation:MinItems=1
3531
// +kubebuilder:validation:MaxItems=16
3632
TargetRefs []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName `json:"targetRefs"`
@@ -47,8 +43,8 @@ type HTTPRoutePolicy struct {
4743
metav1.TypeMeta `json:",inline"`
4844
metav1.ObjectMeta `json:"metadata,omitempty"`
4945

50-
Spec HTTPRoutePolicySpec `json:"spec,omitempty"`
51-
Status gatewayv1alpha2.PolicyStatus `json:"status,omitempty"`
46+
Spec HTTPRoutePolicySpec `json:"spec,omitempty"`
47+
Status PolicyStatus `json:"status,omitempty"`
5248
}
5349

5450
// +kubebuilder:object:root=true

charts/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ nameOverride: ""
22
labelsOverride: {}
33
annotations: {}
44
podAnnotations: {}
5-
controllerName: gateway.api7.io/api7-ingress-controller
5+
controllerName: gateway.apisix.io/api7-ingress-controller
66
replicas: 1
77
admin:
88
key: '' # Pass the admin key generated for the ingress gateway group

config/crd/bases/gateway.apisix.io_httproutepolicies.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,6 @@ spec:
104104
maxItems: 16
105105
minItems: 1
106106
type: array
107-
x-kubernetes-list-map-keys:
108-
- group
109-
- kind
110-
- name
111-
x-kubernetes-list-type: map
112107
vars:
113108
items:
114109
x-kubernetes-preserve-unknown-fields: true

config/samples/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
log_level: "info" # The log level of the API7 Ingress Controller.
22
# the default value is "info".
33

4-
controller_name: gateway.api7.io/api7-ingress-controller # The controller name of the API7 Ingress Controller,
4+
controller_name: gateway.apisix.io/api7-ingress-controller # The controller name of the API7 Ingress Controller,
55
# which is used to identify the controller in the GatewayClass.
66
# The default value is "gateway.api7.io/api7-ingress-controller".
77
leader_election_id: "api7-ingress-controller-leader" # The leader election ID for the API7 Ingress Controller.

docs/configure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The API7 Ingress Controller is a Kubernetes Ingress Controller that implements t
88
log_level: "info" # The log level of the API7 Ingress Controller.
99
# the default value is "info".
1010

11-
controller_name: gateway.api7.io/api7-ingress-controller # The controller name of the API7 Ingress Controller,
11+
controller_name: gateway.apisix.io/api7-ingress-controller # The controller name of the API7 Ingress Controller,
1212
# which is used to identify the controller in the GatewayClass.
1313
# The default value is "gateway.api7.io/api7-ingress-controller".
1414

internal/controller/httproute_controller.go

Lines changed: 151 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
"github.com/go-logr/logr"
9+
"github.com/pkg/errors"
910
corev1 "k8s.io/api/core/v1"
1011
discoveryv1 "k8s.io/api/discovery/v1"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -20,6 +21,7 @@ import (
2021
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2122
"sigs.k8s.io/controller-runtime/pkg/source"
2223
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
24+
"sigs.k8s.io/gateway-api/apis/v1alpha2"
2325

2426
"github.com/api7/api7-ingress-controller/api/v1alpha1"
2527
"github.com/api7/api7-ingress-controller/internal/controller/indexer"
@@ -118,6 +120,23 @@ func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
118120
},
119121
),
120122
).
123+
Watches(&v1alpha1.HTTPRoutePolicy{},
124+
handler.EnqueueRequestsFromMapFunc(r.listHTTPRouteByHTTPRoutePolicy),
125+
builder.WithPredicates(
126+
predicate.Funcs{
127+
CreateFunc: func(e event.CreateEvent) bool {
128+
return true
129+
},
130+
DeleteFunc: func(e event.DeleteEvent) bool {
131+
return true
132+
},
133+
UpdateFunc: r.httpRoutePolicyPredicateOnUpdate,
134+
GenericFunc: func(e event.GenericEvent) bool {
135+
return false
136+
},
137+
},
138+
),
139+
).
121140
WatchesRawSource(
122141
source.Channel(
123142
r.genericEvent,
@@ -127,43 +146,6 @@ func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
127146
Complete(r)
128147
}
129148

130-
func (r *HTTPRouteReconciler) listHTTPRouteForGenericEvent(ctx context.Context, obj client.Object) []reconcile.Request {
131-
var namespacedNameMap = make(map[types.NamespacedName]struct{})
132-
requests := []reconcile.Request{}
133-
switch v := obj.(type) {
134-
case *v1alpha1.BackendTrafficPolicy:
135-
httprouteAll := []gatewayv1.HTTPRoute{}
136-
for _, ref := range v.Spec.TargetRefs {
137-
httprouteList := &gatewayv1.HTTPRouteList{}
138-
if err := r.List(ctx, httprouteList, client.MatchingFields{
139-
indexer.ServiceIndexRef: indexer.GenIndexKey(v.GetNamespace(), string(ref.Name)),
140-
}); err != nil {
141-
r.Log.Error(err, "failed to list HTTPRoutes for BackendTrafficPolicy", "namespace", v.GetNamespace(), "ref", ref.Name)
142-
return nil
143-
}
144-
httprouteAll = append(httprouteAll, httprouteList.Items...)
145-
}
146-
for _, hr := range httprouteAll {
147-
key := types.NamespacedName{
148-
Namespace: hr.Namespace,
149-
Name: hr.Name,
150-
}
151-
if _, ok := namespacedNameMap[key]; !ok {
152-
namespacedNameMap[key] = struct{}{}
153-
requests = append(requests, reconcile.Request{
154-
NamespacedName: client.ObjectKey{
155-
Namespace: hr.Namespace,
156-
Name: hr.Name,
157-
},
158-
})
159-
}
160-
}
161-
default:
162-
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy")
163-
}
164-
return requests
165-
}
166-
167149
func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
168150
hr := new(gatewayv1.HTTPRoute)
169151
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
@@ -228,6 +210,11 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
228210
acceptStatus.msg = err.Error()
229211
}
230212

213+
if err := r.processHTTPRoutePolicies(tctx, hr); err != nil {
214+
acceptStatus.status = false
215+
acceptStatus.msg = err.Error()
216+
}
217+
231218
if err := r.processHTTPRouteBackendRefs(tctx); err != nil {
232219
resolveRefStatus = status{
233220
status: false,
@@ -392,6 +379,105 @@ func (r *HTTPRouteReconciler) listHTTPRoutesForGateway(ctx context.Context, obj
392379
return requests
393380
}
394381

382+
func (r *HTTPRouteReconciler) listHTTPRouteByHTTPRoutePolicy(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
383+
httpRoutePolicy, ok := obj.(*v1alpha1.HTTPRoutePolicy)
384+
if !ok {
385+
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to HTTPRoutePolicy")
386+
return nil
387+
}
388+
389+
var keys = make(map[types.NamespacedName]struct{})
390+
for _, ref := range httpRoutePolicy.Spec.TargetRefs {
391+
if ref.Kind != "HTTPRoute" {
392+
continue
393+
}
394+
key := types.NamespacedName{
395+
Namespace: obj.GetNamespace(),
396+
Name: string(ref.Name),
397+
}
398+
if _, ok := keys[key]; ok {
399+
continue
400+
}
401+
402+
var httpRoute gatewayv1.HTTPRoute
403+
if err := r.Get(ctx, client.ObjectKey{Namespace: key.Namespace, Name: key.Name}, &httpRoute); err != nil {
404+
r.Log.Error(err, "failed to get HTTPRoute by HTTPRoutePolicy targetRef", "namespace", key.Namespace, "name", key.Name)
405+
continue
406+
}
407+
if ref.SectionName != nil {
408+
var matchRuleName bool
409+
for _, rule := range httpRoute.Spec.Rules {
410+
if rule.Name != nil && *rule.Name == *ref.SectionName {
411+
matchRuleName = true
412+
break
413+
}
414+
}
415+
if !matchRuleName {
416+
r.Log.Error(errors.Errorf("failed to get HTTPRoute rule by HTTPRoutePolicy targetRef"), "namespace", key.Namespace, "name", key.Name, "sectionName", *ref.SectionName)
417+
continue
418+
}
419+
}
420+
keys[key] = struct{}{}
421+
requests = append(requests, reconcile.Request{
422+
NamespacedName: types.NamespacedName{
423+
Namespace: key.Namespace,
424+
Name: key.Name,
425+
},
426+
})
427+
}
428+
429+
return requests
430+
}
431+
432+
func (r *HTTPRouteReconciler) listHTTPRouteForGenericEvent(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
433+
var namespacedNameMap = make(map[types.NamespacedName]struct{})
434+
435+
switch v := obj.(type) {
436+
case *v1alpha1.BackendTrafficPolicy:
437+
httprouteAll := []gatewayv1.HTTPRoute{}
438+
for _, ref := range v.Spec.TargetRefs {
439+
httprouteList := &gatewayv1.HTTPRouteList{}
440+
if err := r.List(ctx, httprouteList, client.MatchingFields{
441+
indexer.ServiceIndexRef: indexer.GenIndexKey(v.GetNamespace(), string(ref.Name)),
442+
}); err != nil {
443+
r.Log.Error(err, "failed to list HTTPRoutes for BackendTrafficPolicy", "namespace", v.GetNamespace(), "ref", ref.Name)
444+
return nil
445+
}
446+
httprouteAll = append(httprouteAll, httprouteList.Items...)
447+
}
448+
for _, hr := range httprouteAll {
449+
key := types.NamespacedName{
450+
Namespace: hr.Namespace,
451+
Name: hr.Name,
452+
}
453+
if _, ok := namespacedNameMap[key]; !ok {
454+
namespacedNameMap[key] = struct{}{}
455+
requests = append(requests, reconcile.Request{
456+
NamespacedName: client.ObjectKey{
457+
Namespace: hr.Namespace,
458+
Name: hr.Name,
459+
},
460+
})
461+
}
462+
}
463+
case *v1alpha1.HTTPRoutePolicy:
464+
for _, ref := range v.Spec.TargetRefs {
465+
namespacedName := types.NamespacedName{Namespace: v.GetNamespace(), Name: string(ref.Name)}
466+
if _, ok := namespacedNameMap[namespacedName]; !ok {
467+
namespacedNameMap[namespacedName] = struct{}{}
468+
if err := r.Get(ctx, namespacedName, new(gatewayv1.HTTPRoute)); err != nil {
469+
r.Log.Info("failed to Get HTTPRoute", "namespace", namespacedName.Namespace, "name", namespacedName.Name)
470+
continue
471+
}
472+
requests = append(requests, reconcile.Request{NamespacedName: namespacedName})
473+
}
474+
}
475+
default:
476+
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy")
477+
}
478+
return requests
479+
}
480+
395481
func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.TranslateContext) error {
396482
var terr error
397483
for _, backend := range tctx.BackendRefs {
@@ -454,7 +540,7 @@ func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.Transla
454540
return terr
455541
}
456542

457-
func (t *HTTPRouteReconciler) processHTTPRoute(tctx *provider.TranslateContext, httpRoute *gatewayv1.HTTPRoute) error {
543+
func (r *HTTPRouteReconciler) processHTTPRoute(tctx *provider.TranslateContext, httpRoute *gatewayv1.HTTPRoute) error {
458544
var terror error
459545
for _, rule := range httpRoute.Spec.Rules {
460546
for _, filter := range rule.Filters {
@@ -463,7 +549,7 @@ func (t *HTTPRouteReconciler) processHTTPRoute(tctx *provider.TranslateContext,
463549
}
464550
if filter.ExtensionRef.Kind == "PluginConfig" {
465551
pluginconfig := new(v1alpha1.PluginConfig)
466-
if err := t.Get(context.Background(), client.ObjectKey{
552+
if err := r.Get(context.Background(), client.ObjectKey{
467553
Namespace: httpRoute.GetNamespace(),
468554
Name: string(filter.ExtensionRef.Name),
469555
}, pluginconfig); err != nil {
@@ -508,3 +594,29 @@ func (t *HTTPRouteReconciler) processHTTPRoute(tctx *provider.TranslateContext,
508594

509595
return terror
510596
}
597+
598+
func (r *HTTPRouteReconciler) httpRoutePolicyPredicateOnUpdate(e event.UpdateEvent) bool {
599+
oldPolicy, ok0 := e.ObjectOld.(*v1alpha1.HTTPRoutePolicy)
600+
newPolicy, ok1 := e.ObjectNew.(*v1alpha1.HTTPRoutePolicy)
601+
if !ok0 || !ok1 {
602+
return false
603+
}
604+
var discardsRefs = make(map[string]v1alpha2.LocalPolicyTargetReferenceWithSectionName)
605+
for _, ref := range oldPolicy.Spec.TargetRefs {
606+
key := indexer.GenHTTPRoutePolicyIndexKey(string(ref.Group), string(ref.Kind), e.ObjectOld.GetNamespace(), string(ref.Name), "")
607+
discardsRefs[key] = ref
608+
}
609+
for _, ref := range newPolicy.Spec.TargetRefs {
610+
key := indexer.GenHTTPRoutePolicyIndexKey(string(ref.Group), string(ref.Kind), e.ObjectOld.GetNamespace(), string(ref.Name), "")
611+
delete(discardsRefs, key)
612+
}
613+
if len(discardsRefs) > 0 {
614+
dump := oldPolicy.DeepCopy()
615+
dump.Spec.TargetRefs = make([]v1alpha2.LocalPolicyTargetReferenceWithSectionName, 0, len(discardsRefs))
616+
for _, ref := range discardsRefs {
617+
dump.Spec.TargetRefs = append(dump.Spec.TargetRefs, ref)
618+
}
619+
r.genericEvent <- event.GenericEvent{Object: dump}
620+
}
621+
return true
622+
}

0 commit comments

Comments
 (0)