From 3ab1fceba13e5c03808703cb429146669b00daa4 Mon Sep 17 00:00:00 2001 From: Alexander Nicke Date: Fri, 11 Sep 2026 13:39:31 +0200 Subject: [PATCH 1/2] 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. --- src/code.cloudfoundry.org/gorouter/mbus/subscriber.go | 2 +- 1 file changed, 1 insertion(+), 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"` From de689fc00c0a0ae77f065299c61db46b0ecb7123 Mon Sep 17 00:00:00 2001 From: Tamara Boehm Date: Mon, 14 Sep 2026 11:08:35 +0200 Subject: [PATCH 2/2] fix: test parsing of hash balance as a string --- .../gorouter/mbus/subscriber_test.go | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/code.cloudfoundry.org/gorouter/mbus/subscriber_test.go b/src/code.cloudfoundry.org/gorouter/mbus/subscriber_test.go index 836ff36bc..336859e42 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: "http2", + 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()) + }) }) })