From a9f3d0e96dad3b8cf61ce2d97c84b45a1aa95ce6 Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 08:36:45 +0100 Subject: [PATCH 1/9] chore(ingressroute): remove hashable fields from queue store --- pkg/ingressroute/ingressroute.go | 64 ++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/pkg/ingressroute/ingressroute.go b/pkg/ingressroute/ingressroute.go index b5df5f3..9b2310b 100644 --- a/pkg/ingressroute/ingressroute.go +++ b/pkg/ingressroute/ingressroute.go @@ -51,26 +51,9 @@ func (i *ingressRouteClient) Watch(stopCh chan struct{}) { _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { key, err := cache.MetaNamespaceKeyFunc(obj) - if err != nil { - return + if err == nil { + queue.Add(event{key: key, eventType: watch.Added}) } - - convObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - if err != nil { - return - } - - rawRoutes, found, err := unstructured.NestedSlice(convObj, "spec", "routes") - if err != nil || !found { - return - } - - routes, err := routeInterfaceToMapSlice(rawRoutes) - if err != nil { - return - } - - queue.Add(event{key: key, eventType: watch.Added, routes: routes}) }, UpdateFunc: func(_, newObj interface{}) { key, err := cache.MetaNamespaceKeyFunc(newObj) @@ -96,12 +79,12 @@ func (i *ingressRouteClient) Watch(stopCh chan struct{}) { go informer.Run(stopCh) wait.Until(func() { - for i.processNextItem(queue) { + for i.processNextItem(queue, informer) { } }, time.Second, stopCh) } -func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingInterface[any]) bool { +func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingInterface[any], informer cache.SharedInformer) bool { item, quit := queue.Get() if quit { return false @@ -122,10 +105,44 @@ func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingIn return true } + obj, exists, err := informer.GetStore().GetByKey(event.key) + if err != nil || !exists { + log.Printf("Failed to retrieve object for key %s: %v", event.key, err) + return true + } + + convObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + if err != nil { + log.Printf("Failed to convert unstructured object for key %s: %v", event.key, err) + return true + } + + rawRoutes, found, err := unstructured.NestedSlice(convObj, "spec", "routes") + if err != nil || !found { + log.Printf("No routes found for key %s", event.key) + return true + } + + secretName, found, err := unstructured.NestedString(convObj, "spec", "tls", "secretName") + if err != nil { + log.Printf("Failed to scrape secret name for %s: %v", event.key, err) + return true + } + if !found { + log.Printf("No secret name found, using ingressroute name %s", name) + secretName = name + } + + routes, err := routeInterfaceToMapSlice(rawRoutes) + if err != nil { + log.Printf("Failed to convert routes for key %s: %v", event.key, err) + return true + } + //nolint:exhaustive // ignore missing switch cases switch event.eventType { case watch.Added, watch.Modified: - createErr := i.client.certmanager.Certificates.Create(context.Background(), namespace, name, event.routes) + createErr := i.client.certmanager.Certificates.Create(context.Background(), namespace, secretName, routes) if createErr != nil { if errors.Is(createErr, certmanager.ErrCertificateAlreadyExist) { log.Printf("Certificate %s for %s already exists", secretName, event.key) @@ -134,7 +151,7 @@ func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingIn } } case watch.Deleted: - if deleteErr := i.client.certmanager.Certificates.Delete(context.Background(), namespace, name); deleteErr != nil { + if deleteErr := i.client.certmanager.Certificates.Delete(context.Background(), namespace, secretName); deleteErr != nil { log.Printf("Failed to delete certificate %s: %v", event.key, deleteErr) } } @@ -145,7 +162,6 @@ func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingIn type event struct { key string eventType watch.EventType - routes []map[string]interface{} } func routeInterfaceToMapSlice(input []interface{}) ([]map[string]interface{}, error) { From 7fd44764069b127775fba064b22ce9b41232de3d Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 08:53:05 +0100 Subject: [PATCH 2/9] fix(certmanager): convert certificate to unstructured using pointer --- pkg/certmanager/certificate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/certmanager/certificate.go b/pkg/certmanager/certificate.go index a5c2441..c6fbeeb 100644 --- a/pkg/certmanager/certificate.go +++ b/pkg/certmanager/certificate.go @@ -77,7 +77,7 @@ func (c *certificateClient) Create( }, } - obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(cert) + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&cert) if err != nil { return errors.Join(ErrCertificateToUnstructured, err) } From a2b75bc8324433d9122346927ea5bb32955934b9 Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 09:14:58 +0100 Subject: [PATCH 3/9] fix(certmanager): improve regex to ignore "`" character --- pkg/certmanager/certificate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/certmanager/certificate.go b/pkg/certmanager/certificate.go index c6fbeeb..5df8e88 100644 --- a/pkg/certmanager/certificate.go +++ b/pkg/certmanager/certificate.go @@ -140,6 +140,7 @@ func extractHosts(routes []map[string]interface{}) []string { } if match, ok = route["match"].(string); ok { + match = strings.ReplaceAll(match, "`", "") hostMatches := re.FindAllStringSubmatch(match, -1) for _, match := range hostMatches { if len(match) > 1 { From 6caef548c16f016fbbb34e383e6ded2b157fce2c Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 09:55:55 +0100 Subject: [PATCH 4/9] fix(ingressroute): improve logging & deletion handling --- pkg/ingressroute/ingressroute.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/ingressroute/ingressroute.go b/pkg/ingressroute/ingressroute.go index 9b2310b..45e3f27 100644 --- a/pkg/ingressroute/ingressroute.go +++ b/pkg/ingressroute/ingressroute.go @@ -66,7 +66,7 @@ func (i *ingressRouteClient) Watch(stopCh chan struct{}) { return } - key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) + key, err := cache.MetaNamespaceKeyFunc(obj) if err == nil { queue.Add(event{key: key, eventType: watch.Deleted}) } @@ -150,10 +150,12 @@ func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingIn log.Printf("Failed to create certificate %s: %v", event.key, createErr) } } + log.Printf("Certificate %s for %s created", secretName, event.key) case watch.Deleted: if deleteErr := i.client.certmanager.Certificates.Delete(context.Background(), namespace, secretName); deleteErr != nil { log.Printf("Failed to delete certificate %s: %v", event.key, deleteErr) } + log.Printf("Certificate %s for %s deleted", secretName, event.key) } return true From c50a80834c68ead4c9c3224e86e07d16a71fcbb8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 22 Jan 2025 09:06:00 +0000 Subject: [PATCH 5/9] fix(deps): update module github.com/alecthomas/kong to v1.6.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3d4c05f..ad011f9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.ar21.de/yolokube/go-traefik-certmanager go 1.23.4 require ( - github.com/alecthomas/kong v1.6.0 + github.com/alecthomas/kong v1.6.1 github.com/cert-manager/cert-manager v1.16.2 k8s.io/api v0.32.0 k8s.io/apimachinery v0.32.0 diff --git a/go.sum b/go.sum index bc3fff0..5456efe 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/kong v1.6.0 h1:mwOzbdMR7uv2vul9J0FU3GYxE7ls/iX1ieMg5WIM6gE= -github.com/alecthomas/kong v1.6.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= +github.com/alecthomas/kong v1.6.1 h1:/7bVimARU3uxPD0hbryPE8qWrS3Oz3kPQoxA/H2NKG8= +github.com/alecthomas/kong v1.6.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/cert-manager/cert-manager v1.16.2 h1:c9UU2E+8XWGruyvC/mdpc1wuLddtgmNr8foKdP7a8Jg= From fbccb05e2ead761e74cdd2e375856977abb20dd2 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 22 Jan 2025 09:05:45 +0000 Subject: [PATCH 6/9] fix(deps): update kubernetes packages to v0.32.1 --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index ad011f9..83a8fb5 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.23.4 require ( github.com/alecthomas/kong v1.6.1 github.com/cert-manager/cert-manager v1.16.2 - k8s.io/api v0.32.0 - k8s.io/apimachinery v0.32.0 - k8s.io/client-go v0.32.0 + k8s.io/api v0.32.1 + k8s.io/apimachinery v0.32.1 + k8s.io/client-go v0.32.1 ) require ( diff --git a/go.sum b/go.sum index 5456efe..cf3f2cd 100644 --- a/go.sum +++ b/go.sum @@ -122,14 +122,14 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= -k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= -k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= -k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= From 7839bcf3cb23edbf6094dc00db44b4d8b57bf7f4 Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 08:36:45 +0100 Subject: [PATCH 7/9] chore(ingressroute): remove hashable fields from queue store --- pkg/ingressroute/ingressroute.go | 63 ++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/pkg/ingressroute/ingressroute.go b/pkg/ingressroute/ingressroute.go index b5df5f3..4413391 100644 --- a/pkg/ingressroute/ingressroute.go +++ b/pkg/ingressroute/ingressroute.go @@ -51,31 +51,14 @@ func (i *ingressRouteClient) Watch(stopCh chan struct{}) { _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { key, err := cache.MetaNamespaceKeyFunc(obj) - if err != nil { - return + if err == nil { + queue.Add(event{key: key, eventType: watch.Added, object: &obj}) } - - convObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - if err != nil { - return - } - - rawRoutes, found, err := unstructured.NestedSlice(convObj, "spec", "routes") - if err != nil || !found { - return - } - - routes, err := routeInterfaceToMapSlice(rawRoutes) - if err != nil { - return - } - - queue.Add(event{key: key, eventType: watch.Added, routes: routes}) }, UpdateFunc: func(_, newObj interface{}) { key, err := cache.MetaNamespaceKeyFunc(newObj) if err == nil { - queue.Add(event{key: key, eventType: watch.Modified}) + queue.Add(event{key: key, eventType: watch.Modified, object: &newObj}) } }, DeleteFunc: func(obj interface{}) { @@ -85,7 +68,7 @@ func (i *ingressRouteClient) Watch(stopCh chan struct{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { - queue.Add(event{key: key, eventType: watch.Deleted}) + queue.Add(event{key: key, eventType: watch.Deleted, object: &obj}) } }, }) @@ -122,20 +105,52 @@ func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingIn return true } + convObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(event.object) + if err != nil { + log.Printf("Failed to convert unstructured object for key %s: %v", event.key, err) + return true + } + + rawRoutes, found, err := unstructured.NestedSlice(convObj, "spec", "routes") + if err != nil || !found { + log.Printf("No routes found for key %s", event.key) + return true + } + + secretName, found, err := unstructured.NestedString(convObj, "spec", "tls", "secretName") + if err != nil { + log.Printf("Failed to scrape secret name for %s: %v", event.key, err) + return true + } + if !found { + log.Printf("No secret name found, using ingressroute name %s", name) + secretName = name + } + + routes, err := routeInterfaceToMapSlice(rawRoutes) + if err != nil { + log.Printf("Failed to convert routes for key %s: %v", event.key, err) + return true + } + //nolint:exhaustive // ignore missing switch cases switch event.eventType { case watch.Added, watch.Modified: - createErr := i.client.certmanager.Certificates.Create(context.Background(), namespace, name, event.routes) + createErr := i.client.certmanager.Certificates.Create(context.Background(), namespace, secretName, routes) if createErr != nil { if errors.Is(createErr, certmanager.ErrCertificateAlreadyExist) { log.Printf("Certificate %s for %s already exists", secretName, event.key) } else { log.Printf("Failed to create certificate %s: %v", event.key, createErr) } + } else { + log.Printf("Certificate %s for %s created", secretName, event.key) } case watch.Deleted: - if deleteErr := i.client.certmanager.Certificates.Delete(context.Background(), namespace, name); deleteErr != nil { + if deleteErr := i.client.certmanager.Certificates.Delete(context.Background(), namespace, secretName); deleteErr != nil { log.Printf("Failed to delete certificate %s: %v", event.key, deleteErr) + } else { + log.Printf("Certificate %s for %s deleted", secretName, event.key) } } @@ -145,7 +160,7 @@ func (i *ingressRouteClient) processNextItem(queue workqueue.TypedRateLimitingIn type event struct { key string eventType watch.EventType - routes []map[string]interface{} + object *interface{} } func routeInterfaceToMapSlice(input []interface{}) ([]map[string]interface{}, error) { From 065b04ba391c9aea2bd951d3b880f6d6d5a08476 Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 08:53:05 +0100 Subject: [PATCH 8/9] fix(certmanager): convert certificate to unstructured using pointer --- pkg/certmanager/certificate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/certmanager/certificate.go b/pkg/certmanager/certificate.go index a5c2441..c6fbeeb 100644 --- a/pkg/certmanager/certificate.go +++ b/pkg/certmanager/certificate.go @@ -77,7 +77,7 @@ func (c *certificateClient) Create( }, } - obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(cert) + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&cert) if err != nil { return errors.Join(ErrCertificateToUnstructured, err) } From 0ae856a894fe70136172cb8881aede27e4920ceb Mon Sep 17 00:00:00 2001 From: Tom Neuber Date: Wed, 22 Jan 2025 09:14:58 +0100 Subject: [PATCH 9/9] fix(certmanager): improve regex to ignore "`" character --- pkg/certmanager/certificate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/certmanager/certificate.go b/pkg/certmanager/certificate.go index c6fbeeb..5df8e88 100644 --- a/pkg/certmanager/certificate.go +++ b/pkg/certmanager/certificate.go @@ -140,6 +140,7 @@ func extractHosts(routes []map[string]interface{}) []string { } if match, ok = route["match"].(string); ok { + match = strings.ReplaceAll(match, "`", "") hostMatches := re.FindAllStringSubmatch(match, -1) for _, match := range hostMatches { if len(match) > 1 {