Skip to content

Commit 443aa9a

Browse files
committed
chore: Add GatewayClass protection and deletion logic
Introduce a finalizer to prevent deletion of GatewayClasses still in use by Gateways, and implement corresponding e2e tests to validate the protection mechanism and proper removal flow.
1 parent 356694f commit 443aa9a

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

internal/controller/gatewayclass_congroller.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,26 @@ package controller
33
import (
44
"context"
55
"fmt"
6+
"time"
67

78
"github.com/go-logr/logr"
89
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
910
"k8s.io/apimachinery/pkg/runtime"
11+
"k8s.io/apimachinery/pkg/types"
12+
"k8s.io/client-go/tools/record"
1013
ctrl "sigs.k8s.io/controller-runtime"
1114
"sigs.k8s.io/controller-runtime/pkg/client"
15+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1216
"sigs.k8s.io/controller-runtime/pkg/predicate"
1317
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
1418

1519
"github.com/api7/api7-ingress-controller/internal/controller/config"
1620
)
1721

22+
const (
23+
FinalizerGatewayClassProtection = "apisix.apache.org/gc-protection"
24+
)
25+
1826
// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gatewayclasses,verbs=get;list;watch;update
1927
// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gatewayclasses/status,verbs=get;update
2028

@@ -23,11 +31,13 @@ type GatewayClassReconciler struct { //nolint:revive
2331
client.Client
2432
Scheme *runtime.Scheme
2533

34+
record.EventRecorder
2635
Log logr.Logger
2736
}
2837

2938
// SetupWithManager sets up the controller with the Manager.
3039
func (r *GatewayClassReconciler) SetupWithManager(mgr ctrl.Manager) error {
40+
r.EventRecorder = mgr.GetEventRecorderFor("gatewayclass-controller")
3141
return ctrl.NewControllerManagedBy(mgr).
3242
For(&gatewayv1.GatewayClass{}).
3343
WithEventFilter(predicate.NewPredicateFuncs(r.GatewayClassFilter)).
@@ -41,6 +51,43 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request
4151
return ctrl.Result{}, client.IgnoreNotFound(err)
4252
}
4353

54+
if gc.GetDeletionTimestamp().IsZero() {
55+
if !controllerutil.ContainsFinalizer(gc, FinalizerGatewayClassProtection) {
56+
controllerutil.AddFinalizer(gc, FinalizerGatewayClassProtection)
57+
if err := r.Update(ctx, gc); err != nil {
58+
return ctrl.Result{}, err
59+
}
60+
}
61+
} else {
62+
if controllerutil.ContainsFinalizer(gc, FinalizerGatewayClassProtection) {
63+
var gatewayList gatewayv1.GatewayList
64+
if err := r.List(ctx, &gatewayList); err != nil {
65+
r.Log.Error(err, "failed to list gateways")
66+
return ctrl.Result{}, err
67+
}
68+
var gateways []types.NamespacedName
69+
for _, gateway := range gatewayList.Items {
70+
if string(gateway.Spec.GatewayClassName) == gc.GetName() {
71+
gateways = append(gateways, types.NamespacedName{
72+
Namespace: gateway.GetNamespace(),
73+
Name: gateway.GetName(),
74+
})
75+
}
76+
}
77+
if len(gateways) > 0 {
78+
r.Eventf(gc, "Warning", "DeletionBlocked", "the GatewayClass is still in using by Gateways: %v", gateways)
79+
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
80+
} else {
81+
controllerutil.RemoveFinalizer(gc, FinalizerGatewayClassProtection)
82+
if err := r.Update(ctx, gc); err != nil {
83+
return ctrl.Result{}, err
84+
}
85+
}
86+
}
87+
88+
return ctrl.Result{}, nil
89+
}
90+
4491
condition := meta.Condition{
4592
Type: string(gatewayv1.GatewayClassConditionStatusAccepted),
4693
Status: meta.ConditionTrue,

test/e2e/gatewayapi/gatewayclass.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ metadata:
3131
name: api7-not-accepeted
3232
spec:
3333
controllerName: "apisix.apache.org/not-exist"
34+
`
35+
const defaultGateway = `
36+
apiVersion: gateway.networking.k8s.io/v1
37+
kind: Gateway
38+
metadata:
39+
name: api7ee
40+
spec:
41+
gatewayClassName: api7
42+
listeners:
43+
- name: http1
44+
protocol: HTTP
45+
port: 80
3446
`
3547
It("Create GatewayClass", func() {
3648
By("create default GatewayClass")
@@ -53,5 +65,50 @@ spec:
5365
Expect(gcyaml).To(ContainSubstring(`status: Unknown`), "checking GatewayClass condition status")
5466
Expect(gcyaml).To(ContainSubstring("message: Waiting for controller"), "checking GatewayClass condition message")
5567
})
68+
69+
It("Delete GatewayClass", func() {
70+
By("create default GatewayClass")
71+
err := s.CreateResourceFromStringWithNamespace(defautlGatewayClass, "")
72+
Expect(err).NotTo(HaveOccurred(), "creating GatewayClass")
73+
Eventually(func() string {
74+
spec, err := s.GetResourceYaml("GatewayClass", "api7")
75+
Expect(err).NotTo(HaveOccurred(), "get resource yaml")
76+
return spec
77+
}).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(ContainSubstring(`status: "True"`))
78+
79+
By("create a Gateway")
80+
err = s.CreateResourceFromString(defaultGateway)
81+
Expect(err).NotTo(HaveOccurred(), "creating Gateway")
82+
time.Sleep(time.Second)
83+
84+
By("try to delete the GatewayClass")
85+
_, err = s.RunKubectlAndGetOutput("delete", "GatewayClass", "api7", "--wait=false")
86+
Expect(err).NotTo(HaveOccurred())
87+
88+
_, err = s.GetResourceYaml("GatewayClass", "api7")
89+
Expect(err).NotTo(HaveOccurred(), "get resource yaml")
90+
91+
output, err := s.RunKubectlAndGetOutput("describe", "GatewayClass", "api7")
92+
Expect(err).NotTo(HaveOccurred(), "describe GatewayClass api7")
93+
Expect(output).To(And(
94+
ContainSubstring("Warning"),
95+
ContainSubstring("DeletionBlocked"),
96+
ContainSubstring("gatewayclass-controller"),
97+
ContainSubstring("the GatewayClass is still in using by Gateways"),
98+
))
99+
100+
By("delete the Gateway")
101+
err = s.DeleteResource("Gateway", "api7ee")
102+
Expect(err).NotTo(HaveOccurred(), "deleting Gateway")
103+
time.Sleep(time.Second)
104+
105+
By("try to delete the GatewayClass again")
106+
err = s.DeleteResource("GatewayClass", "api7")
107+
Expect(err).NotTo(HaveOccurred())
108+
109+
_, err = s.GetResourceYaml("GatewayClass", "api7")
110+
Expect(err).To(HaveOccurred(), "get resource yaml")
111+
Expect(err.Error()).To(ContainSubstring("not found"))
112+
})
56113
})
57114
})

0 commit comments

Comments
 (0)