Description
Deleting an ApisixConsumer is processed correctly by the provider (the consumer is removed from APISIX), but ApisixConsumerReconciler still returns the original NotFound error. controller-runtime then rate-limits and retries the same request indefinitely, flooding the logs with:
ERROR controllers.ApisixConsumer failed to get ApisixConsumer {"error": "ApisixConsumer.apisix.apache.org \"<name>\" not found"}
ERROR controller-runtime Reconciler error
INFO provider.client syncing all resources
This is still present on master:
https://github.com/apache/apisix-ingress-controller/blob/master/internal/controller/apisixconsumer_controller.go
Steps to reproduce
-
Create a namespaced ApisixConsumer that AIC watches (any namespace the controller is configured for), for example key-auth with a secretRef.
-
Wait until it is accepted / synced (service_number increases, ingest with the key succeeds).
-
Delete the CR:
kubectl -n <ns> delete apisixconsumer <name>
-
Watch AIC logs.
Expected
After Get returns NotFound and Provider.Delete succeeds, reconcile should return ctrl.Result{}, nil and stop. No further retries for that object.
This is already how ApisixTlsReconciler behaves:
if err := r.Get(ctx, req.NamespacedName, &tls); err != nil {
if client.IgnoreNotFound(err) == nil {
// ...
if err := r.Provider.Delete(ctx, &tls); err != nil {
return ctrl.Result{}, err
}
r.Log.Info("deleted apisix tls", "tls", tls.Name)
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
https://github.com/apache/apisix-ingress-controller/blob/master/internal/controller/apisixtls_controller.go
Actual
ApisixConsumerReconciler.Reconcile (current master):
ac := &apiv2.ApisixConsumer{}
if err := r.Get(ctx, req.NamespacedName, ac); err != nil {
if k8serrors.IsNotFound(err) {
ac.Namespace = req.Namespace
ac.Name = req.Name
ac.TypeMeta = metav1.TypeMeta{
Kind: KindApisixConsumer,
APIVersion: apiv2.GroupVersion.String(),
}
if err := r.Provider.Delete(ctx, ac); err != nil {
r.Log.Error(err, "failed to delete provider", "ApisixConsumer", utils.NamespacedName(ac))
return ctrl.Result{}, err
}
// missing: return ctrl.Result{}, nil
}
r.Log.Error(err, "failed to get ApisixConsumer", "request", req.NamespacedName)
return ctrl.Result{}, err
}
When Get is NotFound and Provider.Delete succeeds, execution falls through and still returns the NotFound error. controller-runtime treats that as a failed reconcile and retries with exponential backoff (observed ~20s, ~41s, then continuing until max delay).
The provider path itself appears fine: each retry is followed by syncing all resources, so the delete is applied (and then re-applied). The object is gone from the API server.
Sample logs
INFO controllers.ApisixConsumer reconcile
{"request": {"name":"cust-8fa274728c24","namespace":"signalgate-consumers"}}
ERROR controllers.ApisixConsumer failed to get ApisixConsumer
{"request": {"name":"cust-8fa274728c24","namespace":"signalgate-consumers"},
"error": "ApisixConsumer.apisix.apache.org \"cust-8fa274728c24\" not found"}
ERROR controller-runtime Reconciler error
{"controller": "apisixconsumer", "controllerKind": "ApisixConsumer",
"namespace": "signalgate-consumers", "name": "cust-8fa274728c24",
"error": "ApisixConsumer.apisix.apache.org \"cust-8fa274728c24\" not found"}
INFO provider.client syncing all resources
INFO provider.client syncing resources for config {"service_number": 27}
Line numbers in a 2.x build match controller/apisixconsumer_controller.go:61 (reconcile) and :77 (failed to get ApisixConsumer).
Restarting the AIC pod clears the workqueue and stops the loop until the next delete.
Suggested fix
Return success after a successful provider delete on NotFound, and only log/return Get errors that are not NotFound:
if err := r.Get(ctx, req.NamespacedName, ac); err != nil {
if k8serrors.IsNotFound(err) {
ac.Namespace = req.Namespace
ac.Name = req.Name
ac.TypeMeta = metav1.TypeMeta{
Kind: KindApisixConsumer,
APIVersion: apiv2.GroupVersion.String(),
}
if err := r.Provider.Delete(ctx, ac); err != nil {
r.Log.Error(err, "failed to delete provider", "ApisixConsumer", utils.NamespacedName(ac))
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
r.Log.Error(err, "failed to get ApisixConsumer", "request", req.NamespacedName)
return ctrl.Result{}, err
}
Please also check other 2.x reconcilers for the same fall-through. ApisixTlsReconciler already returns nil after delete; ApisixConsumer does not.
Environment
- APISIX Ingress Controller: 2.x (controller-runtime
ApisixConsumer reconciler; controller/apisixconsumer_controller.go)
- Trigger:
kubectl delete of ApisixConsumer (or any client that deletes the CR)
- Confirmed against current
master source
Description
Deleting an
ApisixConsumeris processed correctly by the provider (the consumer is removed from APISIX), butApisixConsumerReconcilerstill returns the originalNotFounderror. controller-runtime then rate-limits and retries the same request indefinitely, flooding the logs with:This is still present on
master:https://github.com/apache/apisix-ingress-controller/blob/master/internal/controller/apisixconsumer_controller.go
Steps to reproduce
Create a namespaced
ApisixConsumerthat AIC watches (any namespace the controller is configured for), for example key-auth with asecretRef.Wait until it is accepted / synced (
service_numberincreases, ingest with the key succeeds).Delete the CR:
Watch AIC logs.
Expected
After
GetreturnsNotFoundandProvider.Deletesucceeds, reconcile should returnctrl.Result{}, niland stop. No further retries for that object.This is already how
ApisixTlsReconcilerbehaves:https://github.com/apache/apisix-ingress-controller/blob/master/internal/controller/apisixtls_controller.go
Actual
ApisixConsumerReconciler.Reconcile(currentmaster):When
GetisNotFoundandProvider.Deletesucceeds, execution falls through and still returns theNotFounderror. controller-runtime treats that as a failed reconcile and retries with exponential backoff (observed ~20s, ~41s, then continuing until max delay).The provider path itself appears fine: each retry is followed by
syncing all resources, so the delete is applied (and then re-applied). The object is gone from the API server.Sample logs
Line numbers in a 2.x build match
controller/apisixconsumer_controller.go:61(reconcile) and:77(failed to get ApisixConsumer).Restarting the AIC pod clears the workqueue and stops the loop until the next delete.
Suggested fix
Return success after a successful provider delete on
NotFound, and only log/returnGeterrors that are notNotFound:Please also check other 2.x reconcilers for the same fall-through.
ApisixTlsReconcileralready returnsnilafter delete;ApisixConsumerdoes not.Environment
ApisixConsumerreconciler;controller/apisixconsumer_controller.go)kubectl deleteofApisixConsumer(or any client that deletes the CR)mastersource