From 751d4f9ce779d2f5a25ad5362ac8c2bf79973526 Mon Sep 17 00:00:00 2001 From: Maskym Vavilov Date: Mon, 4 Nov 2024 14:26:57 +0000 Subject: [PATCH] allow for wildcard probes Signed-off-by: Maskym Vavilov --- api/v1alpha1/dnshealthcheckprobe_types.go | 2 +- .../dns-operator.clusterserviceversion.yaml | 2 +- .../kuadrant.io_dnshealthcheckprobes.yaml | 2 +- charts/dns-operator/templates/manifests.yaml | 2 +- .../kuadrant.io_dnshealthcheckprobes.yaml | 2 +- .../controller/dnsrecord_healthchecks_test.go | 38 +++++++++++++++++++ internal/probes/worker.go | 2 + 7 files changed, 45 insertions(+), 5 deletions(-) diff --git a/api/v1alpha1/dnshealthcheckprobe_types.go b/api/v1alpha1/dnshealthcheckprobe_types.go index 971441a..04d774c 100644 --- a/api/v1alpha1/dnshealthcheckprobe_types.go +++ b/api/v1alpha1/dnshealthcheckprobe_types.go @@ -29,7 +29,7 @@ type DNSHealthCheckProbeSpec struct { // +kubebuilder:validation:XValidation:rule="self in [80, 443] || (self >= 1024 && self <= 49151)",message="Only ports 80, 443, 1024-49151 are allowed" Port int `json:"port,omitempty"` // Hostname is the value sent in the host header, to route the request to the correct service - // +kubebuilder:validation:Pattern=`^[a-z][a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$` + // +kubebuilder:validation:Pattern=`^([a-z]|\*.)[a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$` Hostname string `json:"hostname,omitempty"` // Address to connect to the host on (IP Address (A Record) or hostname (CNAME)). // +kubebuilder:validation:Pattern=`^([1-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?|[a-z][a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+)?$` diff --git a/bundle/manifests/dns-operator.clusterserviceversion.yaml b/bundle/manifests/dns-operator.clusterserviceversion.yaml index 362e24d..d524983 100644 --- a/bundle/manifests/dns-operator.clusterserviceversion.yaml +++ b/bundle/manifests/dns-operator.clusterserviceversion.yaml @@ -58,7 +58,7 @@ metadata: capabilities: Basic Install categories: Integration & Delivery containerImage: quay.io/kuadrant/dns-operator:latest - createdAt: "2024-10-18T15:21:24Z" + createdAt: "2024-11-04T14:56:29Z" description: A Kubernetes Operator to manage the lifecycle of DNS resources operators.operatorframework.io/builder: operator-sdk-v1.33.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/manifests/kuadrant.io_dnshealthcheckprobes.yaml b/bundle/manifests/kuadrant.io_dnshealthcheckprobes.yaml index d876137..d9353b3 100644 --- a/bundle/manifests/kuadrant.io_dnshealthcheckprobes.yaml +++ b/bundle/manifests/kuadrant.io_dnshealthcheckprobes.yaml @@ -79,7 +79,7 @@ spec: hostname: description: Hostname is the value sent in the host header, to route the request to the correct service - pattern: ^[a-z][a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$ + pattern: ^([a-z]|\*.)[a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$ type: string interval: description: Interval defines how frequently this probe should execute diff --git a/charts/dns-operator/templates/manifests.yaml b/charts/dns-operator/templates/manifests.yaml index 89cce51..d3ab201 100644 --- a/charts/dns-operator/templates/manifests.yaml +++ b/charts/dns-operator/templates/manifests.yaml @@ -80,7 +80,7 @@ spec: hostname: description: Hostname is the value sent in the host header, to route the request to the correct service - pattern: ^[a-z][a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$ + pattern: ^([a-z]|\*.)[a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$ type: string interval: description: Interval defines how frequently this probe should execute diff --git a/config/crd/bases/kuadrant.io_dnshealthcheckprobes.yaml b/config/crd/bases/kuadrant.io_dnshealthcheckprobes.yaml index 49b88f9..7fbbae8 100644 --- a/config/crd/bases/kuadrant.io_dnshealthcheckprobes.yaml +++ b/config/crd/bases/kuadrant.io_dnshealthcheckprobes.yaml @@ -79,7 +79,7 @@ spec: hostname: description: Hostname is the value sent in the host header, to route the request to the correct service - pattern: ^[a-z][a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$ + pattern: ^([a-z]|\*.)[a-z0-9\-]+\.([a-z][a-z0-9\-]+\.)*[a-z][a-z0-9\-]+$ type: string interval: description: Interval defines how frequently this probe should execute diff --git a/internal/controller/dnsrecord_healthchecks_test.go b/internal/controller/dnsrecord_healthchecks_test.go index 82bdc32..7c43fbb 100644 --- a/internal/controller/dnsrecord_healthchecks_test.go +++ b/internal/controller/dnsrecord_healthchecks_test.go @@ -187,6 +187,44 @@ var _ = Describe("DNSRecordReconciler_HealthChecks", func() { }, TestTimeoutMedium, time.Second).Should(Succeed()) }) + It("Should create wildcard probes", func() { + // make record a wildcard one + dnsRecord.Spec.RootHost = v1alpha1.WildcardPrefix + dnsRecord.Spec.RootHost + dnsRecord.Spec.Endpoints = getTestEndpoints(v1alpha1.WildcardPrefix+testHostname, []string{"172.32.200.1", "172.32.200.2"}) + Expect(k8sClient.Create(ctx, dnsRecord)).To(Succeed()) + + // make probes healthy + Eventually(func(g Gomega) { + probes := &v1alpha1.DNSHealthCheckProbeList{} + + g.Expect(k8sClient.List(ctx, probes, &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{ + ProbeOwnerLabel: BuildOwnerLabelValue(dnsRecord), + }), + Namespace: dnsRecord.Namespace, + })).To(Succeed()) + g.Expect(len(probes.Items)).To(Equal(2)) + + for _, probe := range probes.Items { + probe.Status.Healthy = ptr.To(true) + probe.Status.LastCheckedAt = metav1.Now() + probe.Status.ConsecutiveFailures = 0 + g.Expect(k8sClient.Status().Update(ctx, &probe)).To(Succeed()) + } + }, TestTimeoutMedium, time.Second).Should(Succeed()) + + // make sure we published endpoint + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsRecord), dnsRecord)).To(Succeed()) + g.Expect(dnsRecord.Status.Endpoints).To(ConsistOf( + PointTo(MatchFields(IgnoreExtras, Fields{ + "DNSName": Equal(v1alpha1.WildcardPrefix + testHostname), + "Targets": ConsistOf("172.32.200.1", "172.32.200.2"), + })), + )) + }, TestTimeoutMedium, time.Second).Should(Succeed()) + }) + It("Should remove unhealthy endpoints", func() { //Create default test dnsrecord Expect(k8sClient.Create(ctx, dnsRecord)).To(Succeed()) diff --git a/internal/probes/worker.go b/internal/probes/worker.go index 474de8f..818a079 100644 --- a/internal/probes/worker.go +++ b/internal/probes/worker.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/http" + "strings" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -128,6 +129,7 @@ func (w *Probe) execute(ctx context.Context, probe *v1alpha1.DNSHealthCheckProbe func (w *Probe) performRequest(ctx context.Context, protocol, host, path, ip string, port int, allowInsecure bool, headers v1alpha1.AdditionalHeaders) ProbeResult { logger := log.FromContext(ctx).WithValues("health probe worker:", "preforming request") + host, _ = strings.CutPrefix(host, v1alpha1.WildcardPrefix) probeClient := metrics.NewInstrumentedClient("probe", &http.Client{ Transport: TransportWithDNSResponse(map[string]string{host: ip}, allowInsecure), })