Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions pkg/controller/ingress/ingressclass_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ingress
import (
"context"
"fmt"
"strings"
"time"

"github.com/stackitcloud/application-load-balancer-controller/pkg/controller/ingress/diff"
Expand Down Expand Up @@ -86,23 +87,44 @@ func (r *IngressClassReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, nil
}

requeue, err := r.updateStatus(ctx, ingressClass, alb)
if requeue, err := r.checkStatus(ingressClass, alb); !requeue.IsZero() || err != nil {
return requeue, err
}

err = r.updateStatus(ctx, ingressClass, alb)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to update ingress status: %w", err)
}

return requeue, nil
return ctrl.Result{}, nil
}

// updateStatus updates the status of the Ingresses with the ALB IP address
func (r *IngressClassReconciler) updateStatus(
ctx context.Context, ingressClass *networkingv1.IngressClass, alb *albsdk.LoadBalancer) (ctrl.Result, error) {
if alb.Status == nil || *alb.Status != albsdk.LOADBALANCERSTATUS_STATUS_READY {
r.Recorder.Eventf(ingressClass, nil, corev1.EventTypeNormal, "AlbNotReady", "Reconciling", "ALB is in status %q", ptr.Deref(alb.Status, "unknown"))
// ALB is not yet ready, requeue
return ctrl.Result{RequeueAfter: readyRequeueInterval}, nil
func (r *IngressClassReconciler) checkStatus(ingressClass *networkingv1.IngressClass, alb *albsdk.LoadBalancer) (ctrl.Result, error) {
albStatus := ptr.Deref(alb.Status, "unknown")
Comment thread
dergeberl marked this conversation as resolved.

if albStatus == albsdk.LOADBALANCERSTATUS_STATUS_READY {
return ctrl.Result{}, nil
}

if albStatus == albsdk.LOADBALANCERSTATUS_STATUS_ERROR {
var errMessages []string
for _, lbErr := range alb.GetErrors() {
errMessages = append(errMessages, fmt.Sprintf("[%s] %s", lbErr.GetType(), lbErr.GetDescription()))
}

r.Recorder.Eventf(ingressClass, nil, corev1.EventTypeWarning, "AlbInError", "Reconciling", "ALB is in error state: %s", strings.Join(errMessages, "; "))
// return error to use backoff for retry
Comment thread
dergeberl marked this conversation as resolved.
Outdated
return ctrl.Result{}, fmt.Errorf("alb %s status is error", ptr.Deref(alb.Name, ""))
Comment thread
dergeberl marked this conversation as resolved.
Outdated
Comment thread
dergeberl marked this conversation as resolved.
Outdated
}

r.Recorder.Eventf(ingressClass, nil, corev1.EventTypeNormal, "AlbNotReady", "Reconciling", "ALB is in status %q", albStatus)
// ALB is not yet ready, requeue
return ctrl.Result{RequeueAfter: readyRequeueInterval}, nil
}

// updateStatus updates the status of the Ingresses with the ALB IP address
func (r *IngressClassReconciler) updateStatus(
ctx context.Context, ingressClass *networkingv1.IngressClass, alb *albsdk.LoadBalancer) error {
var albIP string
if alb.ExternalAddress != nil && *alb.ExternalAddress != "" {
albIP = *alb.ExternalAddress
Expand All @@ -112,12 +134,12 @@ func (r *IngressClassReconciler) updateStatus(
}

if albIP == "" {
return ctrl.Result{}, fmt.Errorf("alb %s is ready but has no IPs", ptr.Deref(alb.Name, ""))
return fmt.Errorf("alb %s is ready but has no IPs", ptr.Deref(alb.Name, ""))
}

ingresses, err := r.getIngressesForIngressClass(ctx, ingressClass)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get ingresses: %w", err)
return fmt.Errorf("failed to get ingresses: %w", err)
}

for i := range ingresses {
Expand All @@ -135,11 +157,11 @@ func (r *IngressClassReconciler) updateStatus(
}
patch := client.MergeFrom(before)
if err := r.Client.Status().Patch(ctx, ingress, patch); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to patch ingress status object: %w", err)
return fmt.Errorf("failed to patch ingress status object: %w", err)
}
}

return ctrl.Result{}, nil
return nil
}

func (r *IngressClassReconciler) getIngressesForIngressClass(ctx context.Context, ingressClass *networkingv1.IngressClass) ([]networkingv1.Ingress, error) {
Expand Down
19 changes: 19 additions & 0 deletions pkg/controller/ingress/ingressclass_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,25 @@ var _ = Describe("IngressClassController", func() {
})))
})

It("should log an event when alb is in error state", func(ctx context.Context) {
ingressClass := &networkingv1.IngressClass{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "ingressclass-",
Annotations: map[string]string{
spec.AnnotationNetworkMode: spec.NetworkModeNodePort,
spec.AnnotationExternalIP: fake.NotExistentIP,
},
},
Spec: networkingv1.IngressClassSpec{
Controller: controllerName,
},
}
testutil.CreateKubernetesResourceAndDeferDeletion(ctx, k8sClient, ingressClass)

Eventually(recorder.Events).WithTimeout(5 * time.Second).Should(Receive(Equal(
Comment thread
dergeberl marked this conversation as resolved.
Outdated
"Warning AlbInError ALB is in error state: [TYPE_FIP_NOT_FOUND] IP address 127.0.0.1 not found")))
})

It("should log an event when the ingress class cannot be reconciled due to an invalid configuration", func(ctx context.Context) {
ignoredIngressClass := &networkingv1.IngressClass{
ObjectMeta: metav1.ObjectMeta{
Expand Down
11 changes: 11 additions & 0 deletions pkg/stackit/fake/alb.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"k8s.io/utils/ptr"
)

const NotExistentIP = "127.0.0.1"
Comment thread
dergeberl marked this conversation as resolved.
Outdated

// ALB is an in-memory implementation of stackit.ApplicationLoadBalancerClient.
// The version of the load balancers use the following sequence: "0", "0+1", "0+1+1", "0+1+1+1", ...
type ALB struct {
Expand Down Expand Up @@ -214,6 +216,15 @@ func (a *ALB) materialize(lb *albsdk.LoadBalancer) {
addr := a.ExternalAddress
lb.ExternalAddress = &addr
}
if lb.ExternalAddress != nil && *lb.ExternalAddress == NotExistentIP {
lb.Status = new(albsdk.LOADBALANCERSTATUS_STATUS_ERROR)
lb.Errors = []albsdk.LoadBalancerError{
{
Type: new(albsdk.LOADBALANCERERRORTYPE_TYPE_FIP_NOT_FOUND),
Description: new(fmt.Sprintf("IP address %s not found", *lb.ExternalAddress)),
},
}
}
}

func (a *ALB) record(method string, args ...any) {
Expand Down