diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClient.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClient.java index f722fdf0b..2481fd3aa 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClient.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClient.java @@ -77,7 +77,8 @@ private void addInstancesToList(List instances, String serviceI Response> services = this.client.getHealthServices(serviceId, request); for (HealthService service : services.getValue()) { - instances.add(new ConsulServiceInstance(service, serviceId)); + instances + .add(new ConsulServiceInstance(service, serviceId, this.properties.isMergeTagsEnabled())); } } diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java index 03455e1db..21298f9b9 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java @@ -212,6 +212,12 @@ public class ConsulDiscoveryProperties { */ private int order = 0; + /** + * Enable merge consul tag data with metadata to create service instance (defaults + * to false). + */ + private boolean mergeTagsEnabled = false; + @SuppressWarnings("unused") private ConsulDiscoveryProperties() { this(new InetUtils(new InetUtilsProperties())); @@ -617,6 +623,14 @@ public void setManagementEnableTagOverride(Boolean managementEnableTagOverride) this.managementEnableTagOverride = managementEnableTagOverride; } + public boolean isMergeTagsEnabled() { + return this.mergeTagsEnabled; + } + + public void setMergeTagsEnabled(boolean mergeTagsEnabled) { + this.mergeTagsEnabled = mergeTagsEnabled; + } + @Override public String toString() { return new ToStringCreator(this).append("aclToken", this.aclToken) @@ -628,6 +642,7 @@ public String toString() { .append("enabled", this.enabled).append("enableTagOverride", this.enableTagOverride) .append("failFast", this.failFast).append("hostInfo", this.hostInfo) .append("healthCheckCriticalTimeout", this.healthCheckCriticalTimeout) + .append("mergeTagsEnabled", this.mergeTagsEnabled) .append("healthCheckHeaders", this.healthCheckHeaders) .append("healthCheckInterval", this.healthCheckInterval).append("healthCheckPath", this.healthCheckPath) .append("healthCheckTimeout", this.healthCheckTimeout) diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServiceInstance.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServiceInstance.java index 47310f9a2..abfcc05c6 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServiceInstance.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServiceInstance.java @@ -16,6 +16,7 @@ package org.springframework.cloud.consul.discovery; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -25,6 +26,7 @@ import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.core.style.ToStringCreator; +import org.springframework.util.StringUtils; import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost; @@ -34,14 +36,26 @@ public class ConsulServiceInstance extends DefaultServiceInstance { public ConsulServiceInstance(HealthService healthService, String serviceId) { this(healthService.getService().getId(), serviceId, findHost(healthService), - healthService.getService().getPort(), getSecure(healthService), getMetadata(healthService), - healthService.getService().getTags()); + healthService.getService().getPort(), getSecure(healthService, false), getMetadata(healthService), + healthService.getService().getTags()); this.healthService = healthService; } public ConsulServiceInstance(String instanceId, String serviceId, String host, int port, boolean secure, Map metadata, List tags) { - super(instanceId, serviceId, host, port, secure, metadata); + this(instanceId, serviceId, host, port, secure, metadata, tags, false); + } + + public ConsulServiceInstance(HealthService healthService, String serviceId, boolean mergeTags) { + this(healthService.getService().getId(), serviceId, findHost(healthService), + healthService.getService().getPort(), getSecure(healthService, mergeTags), getMetadata(healthService), + healthService.getService().getTags(), mergeTags); + this.healthService = healthService; + } + + public ConsulServiceInstance(String instanceId, String serviceId, String host, int port, boolean secure, + Map metadata, List tags, boolean mergeTags) { + super(instanceId, serviceId, host, port, secure, mergeTags ? mergeTags(metadata, tags) : metadata); } public ConsulServiceInstance(String instanceId, String serviceId, String host, int port, boolean secure) { @@ -51,6 +65,39 @@ public ConsulServiceInstance(String instanceId, String serviceId, String host, i public ConsulServiceInstance() { } + private static Map mergeTags(Map metadata, List tags) { + Map result = new LinkedHashMap<>(); + + if (metadata != null) { + result.putAll(metadata); + } + + if (tags == null || tags.isEmpty()) { + return result; + } + + for (String tag : tags) { + String[] parts = StringUtils.delimitedListToStringArray(tag, "="); + + switch (parts.length) { + case 0: + break; + case 1: + result.put(parts[0], parts[0]); + break; + case 2: + result.put(parts[0], parts[1]); + break; + default: + String[] end = Arrays.copyOfRange(parts, 1, parts.length); + result.put(parts[0], StringUtils.arrayToDelimitedString(end, "=")); + break; + } + } + + return result; + } + private static Map getMetadata(HealthService healthService) { Map metadata = healthService.getService().getMeta(); if (metadata == null) { @@ -59,10 +106,12 @@ private static Map getMetadata(HealthService healthService) { return metadata; } - private static boolean getSecure(HealthService healthService) { + private static boolean getSecure(HealthService healthService, boolean mergeTags) { boolean secure = false; Map metadata = getMetadata(healthService); - // getMetadata() above returns an empty Map if meta is null + if (mergeTags) { + metadata = mergeTags(metadata, healthService.getService().getTags()); + } if (metadata.containsKey("secure")) { secure = Boolean.parseBoolean(metadata.get("secure")); } @@ -78,8 +127,8 @@ public void setHealthService(HealthService healthService) { } public List getTags() { - if (healthService != null) { - return healthService.getService().getTags(); + if (this.healthService != null) { + return this.healthService.getService().getTags(); } return Collections.emptyList(); } diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java index 2fb42938b..4dbee9576 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java @@ -66,7 +66,8 @@ public Flux getInstances(String serviceId) { return Flux.defer(() -> { List instances = new ArrayList<>(); for (HealthService healthService : getHealthServices(serviceId)) { - instances.add(new ConsulServiceInstance(healthService, serviceId)); + instances.add(new ConsulServiceInstance(healthService, serviceId, + this.properties.isMergeTagsEnabled())); } return Flux.fromIterable(instances); }).onErrorResume(exception -> { diff --git a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientMergeTagsAndMetadataTests.java b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientMergeTagsAndMetadataTests.java new file mode 100644 index 000000000..bf581b584 --- /dev/null +++ b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientMergeTagsAndMetadataTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.consul.discovery; + +import java.util.List; + +import com.ecwid.consul.v1.ConsulClient; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.consul.test.ConsulTestcontainers; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * @author Maciej Hamiga + **/ +@RunWith(SpringRunner.class) +@SpringBootTest(properties = { "spring.application.name=testConsulDiscovery", + "spring.cloud.consul.discovery.prefer-ip-address=true", "spring.cloud.consul.discovery.metadata[foo]=bar", + "spring.cloud.consul.discovery.merge-tags-enabled=true", "spring.cloud.consul.discovery.tags=foo2=bar2,secure=true"}, + classes = ConsulDiscoveryClientMergeTagsAndMetadataTests.MyTestConfig.class, webEnvironment = RANDOM_PORT) +@ContextConfiguration(initializers = ConsulTestcontainers.class) +public class ConsulDiscoveryClientMergeTagsAndMetadataTests { + + @Autowired + private ConsulDiscoveryClient discoveryClient; + + @Autowired + private ConsulClient consulClient; + + @Test + public void bothTagsAndMetadataAreReturnedAsInstanceMetadata() { + List instances = this.discoveryClient.getInstances("testConsulDiscovery"); + assertThat(instances).as("instances was null").isNotNull(); + assertThat(instances.isEmpty()).as("instances was empty").isFalse(); + + ServiceInstance instance = instances.get(0); + assertThat(instance.isSecure()).as("instance was not secure (http)").isTrue(); + assertThat(instance.getMetadata()).containsEntry("foo", "bar"); + assertThat(instance.getMetadata()).containsEntry("foo2", "bar2"); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @EnableDiscoveryClient + public static class MyTestConfig { + + } + +}