From d01e6a5003db1320685fac140f729a6949928c62 Mon Sep 17 00:00:00 2001 From: Ben Nemec Date: Wed, 13 Jan 2021 17:14:43 -0600 Subject: [PATCH] Truncate long service names to 63 characters In some environments, the combination of cluster name and hostname can get long enough that a service name exceeds 63 characters. This is not allowed by the dns library, so we need to ensure that we never pass it such long names. This change shortens the name by hashing it to 32 characters, then sandwiching that between the first 15 and last 16 characters of the original name. This way the likely unique parts of the name are preserved, but the total length of the name is shortened to an allowable length. Something similar to kubernetes->k8s. --- pkg/publisher/publisher.go | 20 +++++++++++++++++++- pkg/publisher/publisher_test.go | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index 582050b..5f484b4 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -1,6 +1,7 @@ package publisher import ( + "crypto/sha256" "fmt" "net" "sync" @@ -14,7 +15,8 @@ var log = logrus.New() func Publish(ip net.IP, iface net.Interface, service Service, shutdown chan struct{}, waitGroup *sync.WaitGroup) (err error) { defer waitGroup.Done() - svcEntry := zeroconf.NewServiceEntry(service.Name, service.SvcType, service.Domain) + svcName := truncateLongServiceName(service.Name) + svcEntry := zeroconf.NewServiceEntry(svcName, service.SvcType, service.Domain) svcEntry.Port = service.Port if ip.To4() != nil { svcEntry.AddrIPv4 = append(svcEntry.AddrIPv4, ip) @@ -105,6 +107,22 @@ func SetLogLevel(level logrus.Level) { log.SetLevel(level) } +// Service names cannot be longer than 63 characters. If we get a service name +// that is longer, hash it to 32 characters, and sandwich that between the +// first 15 and last 16 characters of the original name. This way we preserve +// the likely unique parts of the name while ensuring it does not exceed +// the 63 character limit. +func truncateLongServiceName(serviceName string) (svcName string) { + svcName = serviceName + if len(svcName) > 63 { + value := sha256.Sum256([]byte(svcName)) + hexValue := fmt.Sprintf("%x", value)[:32] + svcName = svcName[:15] + string(hexValue[:]) + svcName[len(svcName)-16:] + log.Printf("Truncating long service name '%s' to '%s'", serviceName, svcName) + } + return svcName +} + // networkInterfacer defines an interface for several net library functions. Production // code will forward to net library functions, and unit tests will override the methods // for testing purposes. diff --git a/pkg/publisher/publisher_test.go b/pkg/publisher/publisher_test.go index 3dd1a35..0bb43f7 100644 --- a/pkg/publisher/publisher_test.go +++ b/pkg/publisher/publisher_test.go @@ -163,3 +163,23 @@ func (networkInterfaceWithInvalidAddr) Addrs(intf *net.Interface) ([]net.Addr, e func (networkInterfaceWithInvalidAddr) Interfaces() ([]net.Interface, error) { return []net.Interface{upIntf}, nil } + +func TestLongNameTruncation(t *testing.T) { + testCases := []struct { + tcase string + name string + expected string + }{ + {"short name", "master-0", "master-0"}, + {"long name", "prefix-someverylongservicenamethatwouldcauseaproblemfordns-suffix", "prefix-somevery47540ff49304816bc5054bb4f3cc9a9clemfordns-suffix"}, + } + for _, tc := range testCases { + result := truncateLongServiceName(tc.name) + if result != tc.expected { + t.Errorf("case[%v]: expected %v, got %v", tc.tcase, tc.expected, result) + } + if len(result) > 63 { + t.Errorf("case[%v]: Truncated name too long: '%v' is %d characters long", tc.tcase, result, len(result)) + } + } +}