diff --git a/src/Discovery/src/Consul/Discovery/ConsulDiscoveryOptions.cs b/src/Discovery/src/Consul/Discovery/ConsulDiscoveryOptions.cs index 236d6d7976..07a24c3091 100644 --- a/src/Discovery/src/Consul/Discovery/ConsulDiscoveryOptions.cs +++ b/src/Discovery/src/Consul/Discovery/ConsulDiscoveryOptions.cs @@ -26,6 +26,11 @@ public class ConsulDiscoveryOptions /// public IList Tags { get; set; } + /// + /// Gets or sets Metadata to use when registering service. + /// + public IDictionary Metadata { get; set; } + public bool UseNetUtils { get; set; } public InetUtils NetUtils { get; set; } diff --git a/src/Discovery/src/Consul/Discovery/ConsulServiceInstance.cs b/src/Discovery/src/Consul/Discovery/ConsulServiceInstance.cs index b60ba39741..cb5b5adc08 100644 --- a/src/Discovery/src/Consul/Discovery/ConsulServiceInstance.cs +++ b/src/Discovery/src/Consul/Discovery/ConsulServiceInstance.cs @@ -28,6 +28,8 @@ public class ConsulServiceInstance : IServiceInstance /// public Uri Uri { get; } + public string[] Tags { get; } + /// public IDictionary Metadata { get; } @@ -40,12 +42,22 @@ public class ConsulServiceInstance : IServiceInstance public ConsulServiceInstance(ServiceEntry serviceEntry) { Host = ConsulServerUtils.FindHost(serviceEntry); - IDictionary metadata = ConsulServerUtils.GetMetadata(serviceEntry); - IsSecure = metadata.TryGetValue("secure", out string secureString) && bool.Parse(secureString); + Tags = serviceEntry.Service.Tags; + Metadata = serviceEntry.Service.Meta; + IsSecure = GetIsSecure(serviceEntry); ServiceId = serviceEntry.Service.Service; Port = serviceEntry.Service.Port; - Metadata = metadata; string scheme = IsSecure ? "https" : "http"; Uri = new Uri($"{scheme}://{Host}:{Port}"); } + + private static bool GetIsSecure(ServiceEntry serviceEntry) + { + if (serviceEntry.Service.Meta == null) + { + return false; + } + + return serviceEntry.Service.Meta.TryGetValue("secure", out string secureString) && bool.Parse(secureString); + } } diff --git a/src/Discovery/src/Consul/Registry/ConsulRegistration.cs b/src/Discovery/src/Consul/Registry/ConsulRegistration.cs index 900b90f4ca..1f037ac5d2 100644 --- a/src/Discovery/src/Consul/Registry/ConsulRegistration.cs +++ b/src/Discovery/src/Consul/Registry/ConsulRegistration.cs @@ -55,6 +55,8 @@ internal ConsulDiscoveryOptions Options /// public Uri Uri => new($"{Options.Scheme}://{Host}:{Port}"); + public string[] Tags { get; private set; } + /// public IDictionary Metadata { get; private set; } @@ -109,7 +111,8 @@ internal void Initialize(AgentServiceRegistration agentServiceRegistration) ServiceId = agentServiceRegistration.Name; Host = agentServiceRegistration.Address; Port = agentServiceRegistration.Port; - Metadata = ConsulServerUtils.GetMetadata(agentServiceRegistration.Tags); + Tags = agentServiceRegistration.Tags; + Metadata = agentServiceRegistration.Meta; } /// @@ -141,6 +144,7 @@ public static ConsulRegistration CreateRegistration(ConsulDiscoveryOptions optio service.Name = NormalizeForConsul(appName); service.Tags = CreateTags(options); + service.Meta = CreateMetadata(options); if (options.Port != 0) { @@ -151,31 +155,44 @@ public static ConsulRegistration CreateRegistration(ConsulDiscoveryOptions optio return new ConsulRegistration(service, options); } - internal static string[] CreateTags(ConsulDiscoveryOptions options) + internal static IDictionary CreateMetadata(ConsulDiscoveryOptions options) { - var tags = new List(); + var metadata = new Dictionary(); - if (options.Tags != null) + if (options.Metadata != null && options.Metadata.Any()) { - tags.AddRange(options.Tags); + foreach (KeyValuePair m in options.Metadata) + { + metadata.Add(m.Key, m.Value); + } } if (!string.IsNullOrEmpty(options.InstanceZone)) { - tags.Add($"{options.DefaultZoneMetadataName}={options.InstanceZone}"); + metadata.Add(options.DefaultZoneMetadataName, options.InstanceZone); } if (!string.IsNullOrEmpty(options.InstanceGroup)) { - tags.Add($"group={options.InstanceGroup}"); + metadata.Add("group", options.InstanceGroup); } - // store the secure flag in the tags so that clients will be able to figure out whether to use http or https automatically + // store the secure flag in the metadata so that clients will be able to figure out whether to use http or https automatically #pragma warning disable S4040 // Strings should be normalized to uppercase - tags.Add($"secure={(options.Scheme == "https").ToString(CultureInfo.InvariantCulture).ToLowerInvariant()}"); + metadata.Add("secure", (options.Scheme == "https").ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); #pragma warning restore S4040 // Strings should be normalized to uppercase - return tags.ToArray(); + return metadata; + } + + internal static string[] CreateTags(ConsulDiscoveryOptions options) + { + if (options.Tags == null || !options.Tags.Any()) + { + return Array.Empty(); + } + + return options.Tags.ToArray(); } internal static string GetInstanceId(ConsulDiscoveryOptions options, IApplicationInstanceInfo applicationInfo) diff --git a/src/Discovery/src/Consul/Util/ConsulServerUtils.cs b/src/Discovery/src/Consul/Util/ConsulServerUtils.cs index 2a3d478678..8717e725af 100644 --- a/src/Discovery/src/Consul/Util/ConsulServerUtils.cs +++ b/src/Discovery/src/Consul/Util/ConsulServerUtils.cs @@ -48,38 +48,4 @@ public static string FixIPv6Address(string address) return address; } - - public static IDictionary GetMetadata(ServiceEntry healthService) - { - return GetMetadata(healthService.Service.Tags); - } - - public static IDictionary GetMetadata(IList tags) - { - var metadata = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (tags != null) - { - foreach (string tag in tags) - { - int index = tag.IndexOf('='); - string key; - string value; - - if (index == -1 || Equals(index + 1, tag.Length)) - { - key = value = tag; - } - else - { - key = tag.Substring(0, index); - value = tag.Substring(index + 1); - } - - metadata[key] = value; - } - } - - return metadata; - } } diff --git a/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs b/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs index b12f1cedbd..ca03321ef5 100644 --- a/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs +++ b/src/Discovery/test/Consul.Test/Discovery/ConsulDiscoveryClientTest.cs @@ -36,10 +36,10 @@ public async Task AddInstancesToListAsync_AddsExpected() Service = "ServiceId", Address = "foo.bar.com", Port = 1234, - Tags = new[] + Meta = new Dictionary { - "foo=bar", - "secure=true" + ["foo"] = "bar", + ["secure"] = "true" } } }, @@ -50,10 +50,10 @@ public async Task AddInstancesToListAsync_AddsExpected() Service = "ServiceId", Address = "foo1.bar1.com", Port = 5678, - Tags = new[] + Meta = new Dictionary { - "bar=foo", - "secure=false" + ["bar"] = "foo", + ["secure"] = "false" } } } @@ -206,10 +206,10 @@ public void GetAllInstances_ReturnsExpected() Service = "ServiceId", Address = "foo.bar.com", Port = 1234, - Tags = new[] + Meta = new Dictionary { - "foo=bar", - "secure=true" + ["foo"] = "bar", + ["secure"] = "true" } } }, @@ -220,10 +220,10 @@ public void GetAllInstances_ReturnsExpected() Service = "ServiceId", Address = "foo1.bar1.com", Port = 5678, - Tags = new[] + Meta = new Dictionary { - "bar=foo", - "secure=false" + ["bar"] = "foo", + ["secure"] = "false" } } } diff --git a/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs b/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs index 6e5d29b3ca..645cc71d2a 100644 --- a/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs +++ b/src/Discovery/test/Consul.Test/Discovery/ConsulServiceInstanceTest.cs @@ -22,8 +22,13 @@ public void Constructor_Initializes() Port = 1234, Tags = new[] { - "foo=bar", - "secure=true" + "tag1", + "tag2" + }, + Meta = new Dictionary + { + ["foo"] = "bar", + ["secure"] = "true" } } }; @@ -33,11 +38,14 @@ public void Constructor_Initializes() Assert.Equal("ServiceId", si.ServiceId); Assert.True(si.IsSecure); Assert.Equal(1234, si.Port); + Assert.Equal(2, si.Tags.Length); + Assert.Contains("tag1", si.Tags); + Assert.Contains("tag2", si.Tags); Assert.Equal(2, si.Metadata.Count); - Assert.Contains("foo", si.Metadata.Keys); - Assert.Contains("secure", si.Metadata.Keys); - Assert.Contains("bar", si.Metadata.Values); - Assert.Contains("true", si.Metadata.Values); + Assert.Contains(si.Metadata, x => x.Key == "foo"); + Assert.Equal("bar", si.Metadata["foo"]); + Assert.Contains(si.Metadata, x => x.Key == "secure"); + Assert.Equal("true", si.Metadata["secure"]); Assert.Equal(new Uri("https://foo.bar.com:1234"), si.Uri); } } diff --git a/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs b/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs index 48be5de3a3..8b95475876 100644 --- a/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs +++ b/src/Discovery/test/Consul.Test/Discovery/ThisServiceInstanceTest.cs @@ -20,9 +20,9 @@ public void Constructor_Initializes() Name = "foobar", Address = "test.foo.bar", Port = 1234, - Tags = new[] + Meta = new Dictionary { - "foo=bar" + ["foo"] = "bar" } }; diff --git a/src/Discovery/test/Consul.Test/Registry/ConsulRegistrationTest.cs b/src/Discovery/test/Consul.Test/Registry/ConsulRegistrationTest.cs index a13da29bc9..4d2b8d8599 100644 --- a/src/Discovery/test/Consul.Test/Registry/ConsulRegistrationTest.cs +++ b/src/Discovery/test/Consul.Test/Registry/ConsulRegistrationTest.cs @@ -37,7 +37,12 @@ public void Constructor_SetsProperties() Port = 1234, Tags = new[] { - "foo=bar" + "tag1", + "tag2" + }, + Meta = new Dictionary + { + ["foo"] = "bar" } }; @@ -47,6 +52,8 @@ public void Constructor_SetsProperties() Assert.Equal("name", reg.ServiceId); Assert.Equal("address", reg.Host); Assert.Equal(1234, reg.Port); + Assert.Contains("tag1", reg.Tags); + Assert.Contains("tag2", reg.Tags); Assert.Single(reg.Metadata); Assert.Contains("foo", reg.Metadata.Keys); Assert.Contains("bar", reg.Metadata.Values); @@ -61,7 +68,8 @@ public void CreateTags_ReturnsExpected() { Tags = new List { - "foo=bar" + "foo", + "bar" }, InstanceZone = "instancezone", InstanceGroup = "instancegroup", @@ -69,11 +77,43 @@ public void CreateTags_ReturnsExpected() }; string[] result = ConsulRegistration.CreateTags(options); - Assert.Equal(4, result.Length); - Assert.Contains("foo=bar", result); - Assert.Contains("zone=instancezone", result); - Assert.Contains("group=instancegroup", result); - Assert.Contains("secure=true", result); + Assert.Equal(2, result.Length); + Assert.Contains("foo", result); + Assert.Contains("bar", result); + } + + [Fact] + public void CreateMetadata_ReturnsExpected() + { + var options = new ConsulDiscoveryOptions + { + Metadata = new Dictionary + { + ["foo"] = "bar", + ["baz"] = "qux" + }, + InstanceZone = "instancezone", + InstanceGroup = "instancegroup", + Scheme = "https" + }; + + IDictionary result = ConsulRegistration.CreateMetadata(options); + Assert.Equal(5, result.Keys.Count); + + Assert.Contains(result, x => x.Key == "foo"); + Assert.Equal("bar", result["foo"]); + + Assert.Contains(result, x => x.Key == "baz"); + Assert.Equal("qux", result["baz"]); + + Assert.Contains(result, x => x.Key == "zone"); + Assert.Equal("instancezone", result["zone"]); + + Assert.Contains(result, x => x.Key == "group"); + Assert.Equal("instancegroup", result["group"]); + + Assert.Contains(result, x => x.Key == "secure"); + Assert.Equal("true", result["secure"]); } [Fact] @@ -117,32 +157,6 @@ public void AppName_SetAsExpected() Assert.Equal("ConsulDiscoveryServiceName", result.Service.Name); } - [Fact] - public void Tags_MapTo_Metadata() - { - // arrange some tags in configuration - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary - { - { "consul:discovery:tags:0", "key0=value0" }, - { "consul:discovery:tags:1", "key1=value1" }, - { "consul:discovery:tags:2", "keyvalue" } - }).Build(); - - // bind to options - var options = new ConsulDiscoveryOptions(); - configurationRoot.Bind(ConsulDiscoveryOptions.ConsulDiscoveryConfigurationPrefix, options); - string[] tags = ConsulRegistration.CreateTags(options); - - // act - get metadata from tags - IDictionary result = ConsulServerUtils.GetMetadata(tags); - Assert.Contains(result, k => k.Key == "key0"); - Assert.Equal("value0", result["key0"]); - Assert.Contains(result, k => k.Key == "key1"); - Assert.Equal("value1", result["key1"]); - Assert.Contains(result, k => k.Key == "keyvalue"); - Assert.Equal("keyvalue", result["keyvalue"]); - } - [Fact] public void GetDefaultInstanceId_ReturnsExpected() {