From e2055553c3154c9ab237c359c956c28105d8cf0d Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:04:08 +0200 Subject: [PATCH] Service Discovery: Expose Instance ID in IServiceInstance --- src/Common/src/Common/Discovery/IServiceInstance.cs | 5 +++++ src/Common/src/Common/PublicAPI.Unshipped.txt | 1 + .../ConfigServerConfigurationProviderTest.cs | 7 ++++--- .../src/Configuration/ConfigurationServiceInstance.cs | 3 +++ src/Discovery/src/Configuration/PublicAPI.Unshipped.txt | 1 + src/Discovery/src/Consul/ConsulServiceInstance.cs | 4 ++++ src/Discovery/src/Consul/Registry/ConsulRegistration.cs | 4 +--- src/Discovery/src/Consul/ThisServiceInstance.cs | 4 ++++ src/Discovery/src/Eureka/EurekaServiceInstance.cs | 2 ++ .../HttpClients/LoadBalancers/ServiceInstancesResolver.cs | 2 ++ .../Consul.Test/Discovery/ConsulDiscoveryClientTest.cs | 8 ++++++++ .../Consul.Test/Discovery/ConsulServiceInstanceTest.cs | 2 ++ .../test/Consul.Test/Discovery/ThisServiceInstanceTest.cs | 1 + .../test/Eureka.Test/EurekaDiscoveryClientTest.cs | 1 + .../test/Eureka.Test/EurekaServiceInstanceTest.cs | 1 + .../LoadBalancers/RoundRobinLoadBalancerTest.cs | 1 + 16 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/Common/src/Common/Discovery/IServiceInstance.cs b/src/Common/src/Common/Discovery/IServiceInstance.cs index 8f23b8ef3d..8a1c5cac8c 100644 --- a/src/Common/src/Common/Discovery/IServiceInstance.cs +++ b/src/Common/src/Common/Discovery/IServiceInstance.cs @@ -11,6 +11,11 @@ public interface IServiceInstance /// string ServiceId { get; } + /// + /// Gets the instance ID as registered by the discovery client. + /// + string InstanceId { get; } + /// /// Gets the hostname of the registered service instance. /// diff --git a/src/Common/src/Common/PublicAPI.Unshipped.txt b/src/Common/src/Common/PublicAPI.Unshipped.txt index 7dc5c58110..0c34fd435f 100644 --- a/src/Common/src/Common/PublicAPI.Unshipped.txt +++ b/src/Common/src/Common/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +Steeltoe.Common.Discovery.IServiceInstance.InstanceId.get -> string! diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs index 6638ca8c82..7bbfb0f21b 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs @@ -373,8 +373,8 @@ public void UpdateSettingsFromDiscovery_UpdatesSettingsCorrectly() List instances = [ - new TestServiceInstance("i1", new Uri("https://foo.bar:8888/"), metadata1), - new TestServiceInstance("i2", new Uri("https://foo.bar.baz:9999/"), metadata2) + new TestServiceInstance("s", "i1", new Uri("https://foo.bar:8888/"), metadata1), + new TestServiceInstance("s", "i2", new Uri("https://foo.bar.baz:9999/"), metadata2) ]; provider.UpdateSettingsFromDiscovery(instances, options); @@ -416,9 +416,10 @@ private static string GetEncodedUserPassword(string user, string password) return Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user}:{password}")); } - private sealed class TestServiceInstance(string serviceId, Uri uri, IReadOnlyDictionary metadata) : IServiceInstance + private sealed class TestServiceInstance(string serviceId, string instanceId, Uri uri, IReadOnlyDictionary metadata) : IServiceInstance { public string ServiceId { get; } = serviceId; + public string InstanceId { get; } = instanceId; public string Host { get; } = uri.Host; public int Port { get; } = uri.Port; public bool IsSecure { get; } = uri.Scheme == Uri.UriSchemeHttps; diff --git a/src/Discovery/src/Configuration/ConfigurationServiceInstance.cs b/src/Discovery/src/Configuration/ConfigurationServiceInstance.cs index 4b96049165..f5ac148f17 100644 --- a/src/Discovery/src/Configuration/ConfigurationServiceInstance.cs +++ b/src/Discovery/src/Configuration/ConfigurationServiceInstance.cs @@ -22,6 +22,9 @@ public sealed class ConfigurationServiceInstance : IServiceInstance [Required] public string? ServiceId { get; set; } + /// + public string InstanceId => string.Empty; + /// [Required] public string? Host { get; set; } diff --git a/src/Discovery/src/Configuration/PublicAPI.Unshipped.txt b/src/Discovery/src/Configuration/PublicAPI.Unshipped.txt index 7dc5c58110..8a7cac8051 100644 --- a/src/Discovery/src/Configuration/PublicAPI.Unshipped.txt +++ b/src/Discovery/src/Configuration/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +Steeltoe.Discovery.Configuration.ConfigurationServiceInstance.InstanceId.get -> string! diff --git a/src/Discovery/src/Consul/ConsulServiceInstance.cs b/src/Discovery/src/Consul/ConsulServiceInstance.cs index 5c31174d2f..bc38ec621f 100644 --- a/src/Discovery/src/Consul/ConsulServiceInstance.cs +++ b/src/Discovery/src/Consul/ConsulServiceInstance.cs @@ -16,6 +16,9 @@ internal sealed class ConsulServiceInstance : IServiceInstance /// public string ServiceId { get; } + /// + public string InstanceId { get; } + /// public string Host { get; } @@ -48,6 +51,7 @@ internal ConsulServiceInstance(ServiceEntry serviceEntry) Metadata = serviceEntry.Service.Meta.AsReadOnly(); IsSecure = serviceEntry.Service.Meta != null && serviceEntry.Service.Meta.TryGetValue("secure", out string? secureString) && bool.Parse(secureString); ServiceId = serviceEntry.Service.Service; + InstanceId = serviceEntry.Service.ID; Port = serviceEntry.Service.Port; Uri = new Uri($"{(IsSecure ? "https" : "http")}://{Host}:{Port}"); } diff --git a/src/Discovery/src/Consul/Registry/ConsulRegistration.cs b/src/Discovery/src/Consul/Registry/ConsulRegistration.cs index 74c0dcf67d..3366692fdf 100644 --- a/src/Discovery/src/Consul/Registry/ConsulRegistration.cs +++ b/src/Discovery/src/Consul/Registry/ConsulRegistration.cs @@ -22,9 +22,7 @@ internal sealed class ConsulRegistration : IServiceInstance /// public string ServiceId { get; } - /// - /// Gets the instance ID as registered by the Consul server. - /// + /// public string InstanceId { get; } /// diff --git a/src/Discovery/src/Consul/ThisServiceInstance.cs b/src/Discovery/src/Consul/ThisServiceInstance.cs index 6453bd6909..20717a7687 100644 --- a/src/Discovery/src/Consul/ThisServiceInstance.cs +++ b/src/Discovery/src/Consul/ThisServiceInstance.cs @@ -15,6 +15,9 @@ internal sealed class ThisServiceInstance : IServiceInstance /// public string ServiceId { get; } + /// + public string InstanceId { get; } + /// public string Host { get; } @@ -35,6 +38,7 @@ public ThisServiceInstance(ConsulRegistration registration) ArgumentNullException.ThrowIfNull(registration); ServiceId = registration.ServiceId; + InstanceId = registration.InstanceId; Host = registration.Host; IsSecure = registration.IsSecure; Port = registration.Port; diff --git a/src/Discovery/src/Eureka/EurekaServiceInstance.cs b/src/Discovery/src/Eureka/EurekaServiceInstance.cs index e6ac9e1928..1a4e843073 100644 --- a/src/Discovery/src/Eureka/EurekaServiceInstance.cs +++ b/src/Discovery/src/Eureka/EurekaServiceInstance.cs @@ -13,6 +13,7 @@ namespace Steeltoe.Discovery.Eureka; internal sealed class EurekaServiceInstance : IServiceInstance { public string ServiceId { get; } + public string InstanceId { get; } public string Host { get; } public int Port { get; } public bool IsSecure { get; } @@ -24,6 +25,7 @@ public EurekaServiceInstance(InstanceInfo instance) ArgumentNullException.ThrowIfNull(instance); ServiceId = instance.AppName; + InstanceId = instance.InstanceId; Host = instance.HostName; Port = GetPort(instance); IsSecure = instance.IsSecurePortEnabled; diff --git a/src/Discovery/src/HttpClients/LoadBalancers/ServiceInstancesResolver.cs b/src/Discovery/src/HttpClients/LoadBalancers/ServiceInstancesResolver.cs index 8f354d8da2..2ea2c03c93 100644 --- a/src/Discovery/src/HttpClients/LoadBalancers/ServiceInstancesResolver.cs +++ b/src/Discovery/src/HttpClients/LoadBalancers/ServiceInstancesResolver.cs @@ -138,6 +138,7 @@ private sealed class JsonSerializableServiceInstance : IServiceInstance // Trust that deserialized instances meet the IServiceInstance contract, so suppress nullability warnings. public string ServiceId { get; set; } = null!; + public string InstanceId { get; set; } = null!; public string Host { get; set; } = null!; public int Port { get; set; } public bool IsSecure { get; set; } @@ -151,6 +152,7 @@ public static JsonSerializableServiceInstance CopyFrom(IServiceInstance instance return new JsonSerializableServiceInstance { ServiceId = instance.ServiceId, + InstanceId = instance.InstanceId, Host = instance.Host, Port = instance.Port, IsSecure = instance.IsSecure, diff --git a/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs b/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs index 88caae557d..7d1e0cb3ff 100644 --- a/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs +++ b/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs @@ -32,6 +32,7 @@ public async Task AddInstancesToListAsync_AddsExpected() Service = new AgentService { Service = "ServiceId", + ID = "Instance1", Address = "foo.bar.com", Port = 1234, Meta = new Dictionary @@ -46,6 +47,7 @@ public async Task AddInstancesToListAsync_AddsExpected() Service = new AgentService { Service = "ServiceId", + ID = "Instance2", Address = "foo1.bar1.com", Port = 5678, Meta = new Dictionary @@ -79,6 +81,7 @@ await discoveryClient.AddInstancesToListAsync(serviceInstances, "ServiceId", Que serviceInstances[0].Host.Should().Be("foo.bar.com"); serviceInstances[0].ServiceId.Should().Be("ServiceId"); + serviceInstances[0].InstanceId.Should().Be("Instance1"); serviceInstances[0].IsSecure.Should().BeTrue(); serviceInstances[0].Port.Should().Be(1234); serviceInstances[0].Metadata.Should().HaveCount(2); @@ -88,6 +91,7 @@ await discoveryClient.AddInstancesToListAsync(serviceInstances, "ServiceId", Que serviceInstances[1].Host.Should().Be("foo1.bar1.com"); serviceInstances[1].ServiceId.Should().Be("ServiceId"); + serviceInstances[1].InstanceId.Should().Be("Instance2"); serviceInstances[1].IsSecure.Should().BeFalse(); serviceInstances[1].Port.Should().Be(5678); serviceInstances[1].Metadata.Should().HaveCount(2); @@ -169,6 +173,7 @@ public async Task GetAllInstances_ReturnsExpected() Service = new AgentService { Service = "ServiceId", + ID = "Instance1", Address = "foo.bar.com", Port = 1234, Meta = new Dictionary @@ -183,6 +188,7 @@ public async Task GetAllInstances_ReturnsExpected() Service = new AgentService { Service = "ServiceId", + ID = "Instance2", Address = "foo1.bar1.com", Port = 5678, Meta = new Dictionary @@ -215,6 +221,7 @@ public async Task GetAllInstances_ReturnsExpected() serviceInstances[0].Host.Should().Be("foo.bar.com"); serviceInstances[0].ServiceId.Should().Be("ServiceId"); + serviceInstances[0].InstanceId.Should().Be("Instance1"); serviceInstances[0].IsSecure.Should().BeTrue(); serviceInstances[0].Port.Should().Be(1234); serviceInstances[0].Metadata.Should().HaveCount(2); @@ -224,6 +231,7 @@ public async Task GetAllInstances_ReturnsExpected() serviceInstances[1].Host.Should().Be("foo1.bar1.com"); serviceInstances[1].ServiceId.Should().Be("ServiceId"); + serviceInstances[1].InstanceId.Should().Be("Instance2"); serviceInstances[1].IsSecure.Should().BeFalse(); serviceInstances[1].Port.Should().Be(5678); serviceInstances[1].Metadata.Should().HaveCount(2); diff --git a/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs b/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs index a9426cd7a2..9f23b3a233 100644 --- a/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs +++ b/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs @@ -16,6 +16,7 @@ public void Constructor_Initializes() Service = new AgentService { Service = "ServiceId", + ID = "Instance1", Address = "foo.bar.com", Port = 1234, Tags = @@ -35,6 +36,7 @@ public void Constructor_Initializes() serviceInstance.Host.Should().Be("foo.bar.com"); serviceInstance.ServiceId.Should().Be("ServiceId"); + serviceInstance.InstanceId.Should().Be("Instance1"); serviceInstance.IsSecure.Should().BeTrue(); serviceInstance.Port.Should().Be(1234); serviceInstance.Tags.Should().HaveCount(2); diff --git a/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs b/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs index 1b4fa260cd..e509ee6919 100644 --- a/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs +++ b/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs @@ -32,6 +32,7 @@ public void Constructor_Initializes() instance.Host.Should().Be("test.foo.bar"); instance.ServiceId.Should().Be("foobar"); + instance.InstanceId.Should().Be("ID"); instance.IsSecure.Should().BeFalse(); instance.Port.Should().Be(1234); instance.Metadata.Should().ContainSingle(); diff --git a/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs b/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs index fc45d1dce7..68e0e762f3 100644 --- a/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs +++ b/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs @@ -167,6 +167,7 @@ public async Task Constructor_Initializes_Correctly() thisService.Metadata.Should().BeEmpty(); thisService.Port.Should().Be(5000); thisService.ServiceId.Should().Be("DEMO"); + thisService.InstanceId.Should().Be($"{instanceOptions.HostName}:demo:5000"); thisService.Uri.Should().Be(new Uri($"http://{instanceOptions.HostName}:5000")); } diff --git a/src/Discovery/test/Eureka.Test/EurekaServiceInstanceTest.cs b/src/Discovery/test/Eureka.Test/EurekaServiceInstanceTest.cs index ce1bb62cd0..2a5403b94f 100644 --- a/src/Discovery/test/Eureka.Test/EurekaServiceInstanceTest.cs +++ b/src/Discovery/test/Eureka.Test/EurekaServiceInstanceTest.cs @@ -27,6 +27,7 @@ public void InstanceWithBothPorts() var serviceInstance = new EurekaServiceInstance(instance); serviceInstance.ServiceId.Should().Be(instance.AppName); + serviceInstance.InstanceId.Should().Be("id"); serviceInstance.Host.Should().Be(instance.HostName); serviceInstance.Port.Should().Be(instance.SecurePort); serviceInstance.IsSecure.Should().BeTrue(); diff --git a/src/Discovery/test/HttpClients.Test/LoadBalancers/RoundRobinLoadBalancerTest.cs b/src/Discovery/test/HttpClients.Test/LoadBalancers/RoundRobinLoadBalancerTest.cs index 0d7a33fbf7..fb9d69fa7b 100644 --- a/src/Discovery/test/HttpClients.Test/LoadBalancers/RoundRobinLoadBalancerTest.cs +++ b/src/Discovery/test/HttpClients.Test/LoadBalancers/RoundRobinLoadBalancerTest.cs @@ -241,6 +241,7 @@ private static IDistributedCache GetCache() private sealed class TestServiceInstance(Uri uri) : IServiceInstance { public string ServiceId => throw new NotImplementedException(); + public string InstanceId => throw new NotImplementedException(); public string Host => throw new NotImplementedException(); public int Port => throw new NotImplementedException(); public bool IsSecure => throw new NotImplementedException();