Skip to content

Commit 3ee00e3

Browse files
Kamil PrzybylKamil Przybyl
authored andcommitted
fix: certificate deletion logic
1 parent 1ac442f commit 3ee00e3

File tree

2 files changed

+31
-53
lines changed

2 files changed

+31
-53
lines changed

pkg/alb/ingress/alb_spec.go

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"crypto/x509"
66
"encoding/pem"
77
"fmt"
8-
"log"
98
"net/netip"
109
"sort"
1110
"strconv"
@@ -299,53 +298,31 @@ func (r *IngressClassReconciler) loadCerts(
299298
return false, certificateIDs, nil
300299
}
301300

302-
// cleanupCerts deletes the certificates from the Certificates API that are no longer associated with any Ingress in the IngressClass
303-
func (r *IngressClassReconciler) cleanupCerts(ctx context.Context, ingressClass *networkingv1.IngressClass, ingresses []*networkingv1.Ingress) error {
304-
// Prepare a map of secret names that are currently being used by the ingresses
305-
usedSecrets := map[string]bool{}
306-
for _, ingress := range ingresses {
307-
for _, tls := range ingress.Spec.TLS {
308-
if tls.SecretName == "" {
309-
continue
310-
}
311-
// Retrieve the TLS Secret
312-
tlsSecret := &corev1.Secret{}
313-
err := r.Client.Get(ctx, types.NamespacedName{Namespace: ingress.Namespace, Name: tls.SecretName}, tlsSecret)
314-
if err != nil {
315-
log.Printf("failed to get TLS secret %s: %v", tls.SecretName, err)
316-
continue
317-
}
318-
certName := getCertName(ingressClass, ingress, tlsSecret)
319-
usedSecrets[certName] = true
320-
}
321-
}
322-
323-
certificatesList, err := r.CertificateClient.ListCertificate(ctx, r.ProjectID, r.Region)
324-
if err != nil {
325-
return fmt.Errorf("failed to list certificates: %w", err)
326-
}
327-
328-
if certificatesList == nil || certificatesList.Items == nil {
329-
return nil // No certificates to clean up
330-
}
331-
for _, cert := range certificatesList.Items {
332-
certID := *cert.Id
333-
certName := *cert.Name
334-
335-
// The certificatesList contains all certificates in the project, so we need to filter them by the ALB IngressClass UID.
336-
if !strings.HasPrefix(certName, generateShortUID(ingressClass.UID)) {
337-
continue
338-
}
339-
340-
// If the tls secret is no longer in referenced, delete the certificate
341-
if _, inUse := usedSecrets[certName]; !inUse {
342-
err := r.CertificateClient.DeleteCertificate(ctx, r.ProjectID, r.Region, certID)
343-
if err != nil {
344-
return fmt.Errorf("failed to delete certificate %s: %v", certName, err)
345-
}
346-
}
347-
}
348-
return nil
301+
// cleanupCerts deletes all certificates from the Certificates API that are associated with this IngressClass.
302+
func (r *IngressClassReconciler) cleanupCerts(ctx context.Context, ingressClass *networkingv1.IngressClass) error {
303+
// We use the IngressClass UID to identify certificates for this specific class.
304+
// A shortened version is used because that is how the names were generated on creation.
305+
// Note: While a UID collision between clusters is technically possible, it is almost impossible in practice.
306+
classPrefix := generateShortUID(ingressClass.UID)
307+
308+
certificatesList, err := r.CertificateClient.ListCertificate(ctx, r.ProjectID, r.Region)
309+
if err != nil {
310+
return fmt.Errorf("failed to list certificates: %w", err)
311+
}
312+
313+
if certificatesList == nil || certificatesList.Items == nil {
314+
return nil // No certificates to clean up
315+
}
316+
317+
for _, cert := range certificatesList.Items {
318+
if strings.HasPrefix(*cert.Name, classPrefix) {
319+
err := r.CertificateClient.DeleteCertificate(ctx, r.ProjectID, r.Region, *cert.Id)
320+
if err != nil {
321+
return fmt.Errorf("failed to delete orphaned certificate %s: %v", *cert.Name, err)
322+
}
323+
}
324+
}
325+
return nil
349326
}
350327

351328
// isCertReady checks if the certificate chain is complete (leaf + intermediates).

pkg/alb/ingress/ingressclass_controller.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939
)
4040

4141
const (
42-
// finalizerName is the name of the finalizer that is added to the IngressClass
42+
// finalizerName is the name of the finalizer that is added to Ingress and IngressClass
4343
finalizerName = "stackit.cloud/alb-ingress"
4444
// controllerName is the name of the ALB controller that the IngressClass should point to for reconciliation
4545
controllerName = "stackit.cloud/alb-ingress"
@@ -105,7 +105,7 @@ func (r *IngressClassReconciler) Reconcile(ctx context.Context, req ctrl.Request
105105
}
106106

107107
if len(albIngressList) < 1 {
108-
err := r.handleIngressClassWithoutIngresses(ctx, albIngressList, ingressClass)
108+
err := r.handleIngressClassWithoutIngresses(ctx, ingressClass)
109109
if err != nil {
110110
return ctrl.Result{}, fmt.Errorf("failed to reconcile %s IngressClass with no Ingresses: %w", getAlbName(ingressClass), err)
111111
}
@@ -281,17 +281,18 @@ func (r *IngressClassReconciler) updateStatus(ctx context.Context, ingresses []*
281281
return ctrl.Result{}, nil
282282
}
283283

284-
// handleIngressClassWithoutIngresses handles the state of the IngressClass that is not referenced by any Ingress
284+
// handleIngressClassWithoutIngresses handles the case where an IngressClass exists
285+
// but is not referenced by any Ingresses. In this scenario, we delete the associated ALB
286+
// and clean up certificates to avoid billing for unused resources.
285287
func (r *IngressClassReconciler) handleIngressClassWithoutIngresses(
286288
ctx context.Context,
287-
ingresses []*networkingv1.Ingress,
288289
ingressClass *networkingv1.IngressClass,
289290
) error {
290291
err := r.ALBClient.DeleteLoadBalancer(ctx, r.ProjectID, r.Region, getAlbName(ingressClass))
291292
if err != nil {
292293
return fmt.Errorf("failed to delete load balancer: %w", err)
293294
}
294-
err = r.cleanupCerts(ctx, ingressClass, ingresses)
295+
err = r.cleanupCerts(ctx, ingressClass)
295296
if err != nil {
296297
return fmt.Errorf("failed to clean up certificates: %w", err)
297298
}

0 commit comments

Comments
 (0)