From 3bbe816869458627e57fb98afb1816d54212cf7c Mon Sep 17 00:00:00 2001 From: Tamara Boehm Date: Mon, 14 Sep 2026 12:20:49 +0200 Subject: [PATCH] fix: accept hash_balance as string Since v0.386.0, gorouter fails to unmarshal `router.register` NATS messages for any route configured with hash-based load balancing (`loadbalancing: hash`) that includes a `hash_balance` factor. The message is rejected as a validation error and dropped, so the endpoint is never registered. The route becomes unreachable (404 / stale until TTL expiry) even though the app and the registration message are healthy. This restores the old parsing. Co-authored-by: Alexander Nicke --- .../gorouter/mbus/subscriber.go | 2 +- .../gorouter/mbus/subscriber_test.go | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/code.cloudfoundry.org/gorouter/mbus/subscriber.go b/src/code.cloudfoundry.org/gorouter/mbus/subscriber.go index 9c9f0dca2..4871b86b3 100644 --- a/src/code.cloudfoundry.org/gorouter/mbus/subscriber.go +++ b/src/code.cloudfoundry.org/gorouter/mbus/subscriber.go @@ -43,7 +43,7 @@ type RegistryMessage struct { type RegistryMessageOpts struct { LoadBalancingAlgorithm string `json:"loadbalancing"` HashHeaderName string `json:"hash_header"` - HashBalance float64 `json:"hash_balance"` + HashBalance float64 `json:"hash_balance,string"` // RFC route policy options (from Cloud Controller via Diego sync) RoutePolicyScope string `json:"route_policy_scope,omitempty"` RoutePolicySources string `json:"route_policy_sources,omitempty"` diff --git a/src/code.cloudfoundry.org/gorouter/mbus/subscriber_test.go b/src/code.cloudfoundry.org/gorouter/mbus/subscriber_test.go index 836ff36bc..f50fbd451 100644 --- a/src/code.cloudfoundry.org/gorouter/mbus/subscriber_test.go +++ b/src/code.cloudfoundry.org/gorouter/mbus/subscriber_test.go @@ -731,6 +731,35 @@ var _ = Describe("Subscriber", func() { Expect(expectedEndpoint.HashBalanceFactor).To(Equal(0.0)) Expect(originalEndpoint).To(Equal(expectedEndpoint)) }) + + It("parses hash_balance from incoming JSON as a string", func() { + data := []byte(`{"host":"host","app":"app","uris":["test.example.com"],"options":{"loadbalancing":"hash","hash_header":"X-Header","hash_balance":"1.5"}}`) + + err := natsClient.Publish("router.register", data) + Expect(err).ToNot(HaveOccurred()) + + Eventually(registry.RegisterCallCount).Should(Equal(1)) + _, originalEndpoint := registry.RegisterArgsForCall(0) + expectedEndpoint := route.NewEndpoint(&route.EndpointOpts{ + Host: "host", + AppId: "app", + Protocol: "http1", + LoadBalancingAlgorithm: "hash", + HashHeaderName: "X-Header", + HashBalanceFactor: 1.5, + }) + + Expect(originalEndpoint).To(Equal(expectedEndpoint)) + }) + + It("rejects route if hash_balance value is not a string", func() { + data := []byte(`{"host":"host","app":"app","uris":["test.example.com"],"options":{"loadbalancing":"hash","hash_header":"X-Header","hash_balance":1.5}}`) + + err := natsClient.Publish("router.register", data) + Expect(err).ToNot(HaveOccurred()) + + Consistently(registry.RegisterCallCount).Should(BeZero()) + }) }) })