From 75467572e944380f14cd6ca7f0be444d185d42e2 Mon Sep 17 00:00:00 2001 From: Asma Bahri Date: Wed, 20 Aug 2025 11:21:40 +0200 Subject: [PATCH] feat(engine): add checkHostByName to resolve hostnames with DNS error handling Signed-off-by: Asma Bahri --- pkg/engine/engine.go | 3 +++ pkg/engine/funcs.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 6e47a0e39..41e65ca30 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -259,6 +259,9 @@ func (e Engine) initFunMap(t *template.Template) { funcMap["getHostByName"] = func(_ string) string { return "" } + funcMap["checkHostByName"] = func(_ string) string { + return "" + } } // Set custom template funcs diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go index a97f8f104..8c888772c 100644 --- a/pkg/engine/funcs.go +++ b/pkg/engine/funcs.go @@ -60,6 +60,7 @@ func funcMap() template.FuncMap { "mustToJson": mustToJSON, "fromJson": fromJSON, "fromJsonArray": fromJSONArray, + "checkHostByName": checkHostByName, // This is a placeholder for the "include" function, which is // late-bound to a template. By declaring it here, we preserve the @@ -232,3 +233,34 @@ func fromJSONArray(str string) []interface{} { } return a } + +// checkHostByName resolves a hostname to its first IP address. +// +// - If the host is found, it returns the IP address. +// - If the host does not exist (NXDOMAIN), it returns an empty string. +// - If DNS fails for other reasons (SERVFAIL, timeout, etc.), it returns an error and aborts rendering. +// +// This function was introduced because Sprig's getHostByName silently swallows DNS errors. +// It retries DNS resolution up to 3 times on transient errors, with a 15-second delay between attempts. +func checkHostByName(name string) (string, error) { + var lastErr error + for tries := 3; tries > 0; tries-- { + addrs, err := net.LookupHost(name) + if err == nil && len(addrs) > 0 { + return addrs[0], nil + } + lastErr = err + + dnsErr, ok := err.(*net.DNSError) + if !ok { + return "", fmt.Errorf("checkHostByName: DNS error: %v", err) + } + if dnsErr.IsNotFound { + return "", nil // NXDOMAIN is not considered fatal + } + + time.Sleep(15 * time.Second) + } + + return "", fmt.Errorf("checkHostByName: DNS resolution failed for %s: %v", name, lastErr) +}