Implement ServiceInstance.getMetadata

Consul does not yet support metadata on services. Spring Cloud's `ServiceInstance` has a `Map<String, String> metadata` field. Spring Cloud Consul uses Consul tags to approximate metadata until Consul officially supports metadata. Tags with the form `key=value` will be split and used as a `Map` key and value respectively. Tags without the equal `=` sign, will be used as both the key and value.

fixes gh-162
This commit is contained in:
Spencer Gibb
2016-04-04 21:06:58 -06:00
parent 02d740fdf9
commit a78a567cce
5 changed files with 107 additions and 16 deletions

View File

@@ -83,6 +83,21 @@ spring:
----
==== Metadata and Consul tags
Consul does not yet support metadata on services. Spring Cloud's `ServiceInstance` has a `Map<String, String> metadata` field. Spring Cloud Consul uses Consul tags to approximate metadata until Consul officially supports metadata. Tags with the form `key=value` will be split and used as a `Map` key and value respectively. Tags without the equal `=` sign, will be used as both the key and value.
.application.yml
----
spring:
cloud:
consul:
discovery:
tags: foo=bar, baz
----
The above configuration will result in a map with `foo->bar` and `baz->baz`.
==== Making the Consul Instance ID Unique
By default a consul instance is registered with an ID that is equal to its Spring Application Context ID. By default, the Spring Application Context ID is `${spring.application.name}:comma,separated,profiles:${server.port}`. For most cases, this will allow multiple instances of one service to run on one machine. If further uniqueness is required, Using Spring Cloud you can override this by providing a unique identifier in `spring.cloud.consul.discovery.instanceId`. For example:

View File

@@ -20,6 +20,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.util.StringUtils;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
@@ -28,15 +34,10 @@ import com.ecwid.consul.v1.agent.model.Self;
import com.ecwid.consul.v1.agent.model.Service;
import com.ecwid.consul.v1.health.model.HealthService;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.util.StringUtils;
import lombok.extern.apachecommons.CommonsLog;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.getMetadata;
/**
* @author Spencer Gibb
@@ -74,6 +75,7 @@ public class ConsulDiscoveryClient implements DiscoveryClient {
Service service = agentServices.getValue().get(lifecycle.getServiceId());
String serviceId;
Integer port;
Map<String, String> metadata;
if (service == null) {
// possibly called before registration
log.warn("Unable to locate service in consul agent: "
@@ -85,10 +87,12 @@ public class ConsulDiscoveryClient implements DiscoveryClient {
&& serverProperties.getPort() != null) {
port = serverProperties.getPort();
}
metadata = getMetadata(this.properties.getTags());
}
else {
serviceId = service.getId();
port = service.getPort();
metadata = getMetadata(service.getTags());
}
String host = "localhost";
Response<Self> agentSelf = client.getAgentSelf();
@@ -101,7 +105,7 @@ public class ConsulDiscoveryClient implements DiscoveryClient {
host = member.getName();
}
}
return new DefaultServiceInstance(serviceId, host, port, false);
return new DefaultServiceInstance(serviceId, host, port, false, metadata);
}
@Override
@@ -119,7 +123,7 @@ public class ConsulDiscoveryClient implements DiscoveryClient {
for (HealthService service : services.getValue()) {
String host = findHost(service);
instances.add(new DefaultServiceInstance(serviceId, host, service
.getService().getPort(), false));
.getService().getPort(), false, getMetadata(service)));
}
}

View File

@@ -16,12 +16,14 @@
package org.springframework.cloud.consul.discovery;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost;
import java.util.Map;
import com.ecwid.consul.v1.health.model.Check;
import com.ecwid.consul.v1.health.model.HealthService;
import com.netflix.loadbalancer.Server;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost;
/**
* @author Spencer Gibb
*/
@@ -67,6 +69,10 @@ public class ConsulServer extends Server {
return this.service;
}
public Map<String, String> getMetadata() {
return ConsulServerUtils.getMetadata(this.service);
}
public boolean isPassingChecks() {
for (Check check : this.service.getChecks()) {
if (check.getStatus() != Check.CheckStatus.PASSING) {

View File

@@ -16,6 +16,11 @@
package org.springframework.cloud.consul.discovery;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.ecwid.consul.v1.health.model.HealthService;
import org.springframework.util.StringUtils;
@@ -39,4 +44,34 @@ public class ConsulServerUtils {
}
return node.getNode();
}
public static Map<String, String> getMetadata(HealthService healthService) {
return getMetadata(healthService.getService().getTags());
}
public static Map<String, String> getMetadata(List<String> tags) {
LinkedHashMap<String, String> metadata = new LinkedHashMap<>();
if (tags != null) {
for (String tag : tags) {
String[] parts = StringUtils.delimitedListToStringArray(tag, "=");
switch (parts.length) {
case 0:
break;
case 1:
metadata.put(parts[0], parts[0]);
break;
case 2:
metadata.put(parts[0], parts[1]);
break;
default:
String[] end = Arrays.copyOfRange(parts, 1, parts.length);
metadata.put(parts[0], StringUtils.arrayToDelimitedString(end, "="));
break;
}
}
}
return metadata;
}
}

View File

@@ -16,11 +16,8 @@
package org.springframework.cloud.consul.discovery;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.util.List;
import java.util.Map;
import org.apache.http.conn.util.InetAddressUtils;
import org.junit.Test;
@@ -36,12 +33,18 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
/**
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ConsulDiscoveryClientCustomizedTests.MyTestConfig.class)
@WebIntegrationTest(value = {"spring.application.name=testConsulDiscovery2", "spring.cloud.consul.discovery.instanceId=testConsulDiscovery2Id"}, randomPort = true)
@WebIntegrationTest(value = { "spring.application.name=testConsulDiscovery2",
"spring.cloud.consul.discovery.instanceId=testConsulDiscovery2Id",
"spring.cloud.consul.discovery.tags=plaintag,foo=bar,foo2=bar2=baz2" }, randomPort = true)
public class ConsulDiscoveryClientCustomizedTests {
@Autowired
@@ -55,7 +58,33 @@ public class ConsulDiscoveryClientCustomizedTests {
}
private void assertNotIpAddress(ServiceInstance instance) {
assertFalse("host is an ip address", InetAddressUtils.isIPv4Address(instance.getHost()));
assertFalse("host is an ip address",
InetAddressUtils.isIPv4Address(instance.getHost()));
}
@Test
public void getMetadataWorks() throws InterruptedException {
List<ServiceInstance> instances = discoveryClient
.getInstances("testConsulDiscovery2");
assertNotNull("instances was null", instances);
assertFalse("instances was empty", instances.isEmpty());
ServiceInstance instance = instances.get(0);
assertInstance(instance);
}
private void assertInstance(ServiceInstance instance) {
Map<String, String> metadata = instance.getMetadata();
assertNotNull("metadata was null", metadata);
String foo = metadata.get("foo");
assertEquals("metadata key foo was wrong", "bar", foo);
String plaintag = metadata.get("plaintag");
assertEquals("metadata key plaintag was wrong", "plaintag", plaintag);
String foo2 = metadata.get("foo2");
assertEquals("metadata key foo2 was wrong", "bar2=baz2", foo2);
}
@Test
@@ -63,7 +92,9 @@ public class ConsulDiscoveryClientCustomizedTests {
ServiceInstance instance = discoveryClient.getLocalServiceInstance();
assertNotNull("instance was null", instance);
assertNotIpAddress(instance);
assertEquals("instance id was wrong", "testConsulDiscovery2Id", instance.getServiceId());
assertEquals("instance id was wrong", "testConsulDiscovery2Id",
instance.getServiceId());
assertInstance(instance);
}
@Configuration