Commit 5cd88deb by Dave Syer

Use custom DataCenterInfo to make instance ID unique

The problem manifests itself as errors in the Eureka server log (gh-47), but originates in the client because it is sending requests to /eureka/apps/{NAME}/{ID} with the wrong ID. The InstanceInfo has an ID that is derived (for preference) from the EurekaInstanceConfig.dataCenterInfo, so that's the best way to fix it. See gh-63, Fixes gh-47
parent dc1aab94
......@@ -24,6 +24,7 @@ import com.thoughtworks.xstream.MarshallingStrategy;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterLookup;
import com.thoughtworks.xstream.converters.DataHolder;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.core.TreeMarshallingStrategy;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
......@@ -57,7 +58,8 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
@Override
public void marshal(HierarchicalStreamWriter writer, Object obj,
ConverterLookup converterLookup, Mapper mapper, DataHolder dataHolder) {
delegate.marshal(writer, obj, converterLookup, mapper, dataHolder);
ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup);
delegate.marshal(writer, obj, wrapped, mapper, dataHolder);
}
public static class InstanceIdDataCenterInfo implements DataCenterInfo,
......@@ -101,6 +103,22 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
}
private static class DataCenterAwareConverter extends InstanceInfoConverter {
@Override
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
InstanceInfo info = (InstanceInfo) source;
String instanceId = info.getMetadata().get("instanceId");
DataCenterInfo dataCenter = info.getDataCenterInfo();
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
String old = info.getId();
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
info = new InstanceInfo.Builder(info).setDataCenterInfo(
new InstanceIdDataCenterInfo(id)).build();
source = info;
}
super.marshal(source, writer, context);
}
@Override
public Object unmarshal(HierarchicalStreamReader reader,
......@@ -111,8 +129,9 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy {
DataCenterInfo dataCenter = info.getDataCenterInfo();
if (instanceId != null && Name.Amazon != dataCenter.getName()) {
String old = info.getId();
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
info = new InstanceInfo.Builder(info).setDataCenterInfo(
new InstanceIdDataCenterInfo(old + ":" + instanceId)).build();
new InstanceIdDataCenterInfo(id)).build();
obj = info;
}
return obj;
......
......@@ -33,6 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.appinfo.UniqueIdentifier;
/**
* @author Dave Syer
......@@ -76,10 +77,7 @@ public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
private Map<String, String> metadataMap = new HashMap<>();
private DataCenterInfo dataCenterInfo = new DataCenterInfo() {
@Getter @Setter
private Name name = Name.MyOwn;
};
private DataCenterInfo dataCenterInfo = new IdentifyingDataCenterInfo();
private String ipAddress = hostInfo[0];
......@@ -130,4 +128,21 @@ public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
return preferIpAddress ? ipAddress : hostname;
}
private final class IdentifyingDataCenterInfo implements DataCenterInfo, UniqueIdentifier {
@Getter @Setter
private Name name = Name.MyOwn;
@Override
public String getId() {
String instanceId = metadataMap.get("instanceId");
if (instanceId != null) {
String old = hostname;
String id = old.endsWith(instanceId) ? old : old + ":" + instanceId;
return id;
}
return hostname;
}
}
}
......@@ -22,10 +22,12 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractApplicationContext;
import com.netflix.appinfo.UniqueIdentifier;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import static org.springframework.boot.test.EnvironmentTestUtils.*;
/**
......@@ -44,102 +46,101 @@ public class EurekaInstanceConfigBeanTests {
}
@Test
public void idFromInstanceId() throws Exception {
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean();
instance.getMetadataMap().put("instanceId", "foo");
instance.setHostname("bar");
assertEquals("bar:foo", ((UniqueIdentifier) instance.getDataCenterInfo()).getId());
}
@Test
public void basicBinding() {
addEnvironment(context, "eureka.instance.appGroupName=mygroup");
setupContext();
setupContext();
assertEquals("mygroup", getInstanceConfig().getAppGroupName());
}
@Test
public void nonSecurePort() {
testNonSecurePort("eureka.instance.nonSecurePort");
}
@Test
public void nonSecurePort2() {
testNonSecurePort("server.port");
}
@Test
public void nonSecurePort3() {
testNonSecurePort("SERVER_PORT");
}
@Test
public void nonSecurePort4() {
testNonSecurePort("PORT");
}
private void testNonSecurePort(String propName) {
addEnvironment(context, propName + ":8888");
setupContext();
assertEquals(8888, getInstanceConfig().getNonSecurePort());
}
@Test
public void testDefaultInitialStatus() {
setupContext();
assertEquals("initialStatus wrong", InstanceStatus.UP,
getInstanceConfig().getInitialStatus());
}
@Test(expected = BeanCreationException.class)
public void testBadInitialStatus() {
addEnvironment(context, "eureka.instance.initial-status:FOO");
setupContext();
}
@Test
public void testCustomInitialStatus() {
addEnvironment(context, "eureka.instance.initial-status:STARTING");
setupContext();
assertEquals("initialStatus wrong", InstanceStatus.STARTING,
getInstanceConfig().getInitialStatus());
}
private void setupContext() {
context.register(PropertyPlaceholderAutoConfiguration.class,
TestConfiguration.class);
context.refresh();
}
protected EurekaInstanceConfigBean getInstanceConfig() {
return context.getBean(EurekaInstanceConfigBean.class);
}
/*
@Test
public void serviceUrlWithCompositePropertySource() {
CompositePropertySource source = new CompositePropertySource("composite");
context.getEnvironment().getPropertySources().addFirst(source);
source.addPropertySource(new MapPropertySource("config", Collections
.<String, Object> singletonMap("eureka.client.serviceUrl.defaultZone",
"http://example.com")));
context.register(PropertyPlaceholderAutoConfiguration.class,
TestConfiguration.class);
context.refresh();
assertEquals("{defaultZone=http://example.com}",
context.getBean(EurekaInstanceConfigBean.class).getServiceUrl().toString());
assertEquals(
"[http://example.com]",
context.getBean(EurekaInstanceConfigBean.class)
.getEurekaServerServiceUrls("defaultZone").toString());
}
@Test
public void serviceUrlWithDefault() {
EnvironmentTestUtils.addEnvironment(context,
"eureka.client.serviceUrl.defaultZone:",
"eureka.client.serviceUrl.default:http://example.com");
context.register(PropertyPlaceholderAutoConfiguration.class,
TestConfiguration.class);
context.refresh();
assertEquals(
"[http://example.com]",
context.getBean(EurekaInstanceConfigBean.class)
.getEurekaServerServiceUrls("defaultZone").toString());
}
*/
testNonSecurePort("eureka.instance.nonSecurePort");
}
@Test
public void nonSecurePort2() {
testNonSecurePort("server.port");
}
@Test
public void nonSecurePort3() {
testNonSecurePort("SERVER_PORT");
}
@Test
public void nonSecurePort4() {
testNonSecurePort("PORT");
}
private void testNonSecurePort(String propName) {
addEnvironment(context, propName + ":8888");
setupContext();
assertEquals(8888, getInstanceConfig().getNonSecurePort());
}
@Test
public void testDefaultInitialStatus() {
setupContext();
assertEquals("initialStatus wrong", InstanceStatus.UP, getInstanceConfig()
.getInitialStatus());
}
@Test(expected = BeanCreationException.class)
public void testBadInitialStatus() {
addEnvironment(context, "eureka.instance.initial-status:FOO");
setupContext();
}
@Test
public void testCustomInitialStatus() {
addEnvironment(context, "eureka.instance.initial-status:STARTING");
setupContext();
assertEquals("initialStatus wrong", InstanceStatus.STARTING, getInstanceConfig()
.getInitialStatus());
}
private void setupContext() {
context.register(PropertyPlaceholderAutoConfiguration.class,
TestConfiguration.class);
context.refresh();
}
protected EurekaInstanceConfigBean getInstanceConfig() {
return context.getBean(EurekaInstanceConfigBean.class);
}
/*
* @Test public void serviceUrlWithCompositePropertySource() { CompositePropertySource
* source = new CompositePropertySource("composite");
* context.getEnvironment().getPropertySources().addFirst(source);
* source.addPropertySource(new MapPropertySource("config", Collections .<String,
* Object> singletonMap("eureka.client.serviceUrl.defaultZone",
* "http://example.com")));
* context.register(PropertyPlaceholderAutoConfiguration.class,
* TestConfiguration.class); context.refresh();
* assertEquals("{defaultZone=http://example.com}",
* context.getBean(EurekaInstanceConfigBean.class).getServiceUrl().toString());
* assertEquals( "[http://example.com]",
* context.getBean(EurekaInstanceConfigBean.class)
* .getEurekaServerServiceUrls("defaultZone").toString()); }
*
* @Test public void serviceUrlWithDefault() {
* EnvironmentTestUtils.addEnvironment(context,
* "eureka.client.serviceUrl.defaultZone:",
* "eureka.client.serviceUrl.default:http://example.com");
* context.register(PropertyPlaceholderAutoConfiguration.class,
* TestConfiguration.class); context.refresh(); assertEquals( "[http://example.com]",
* context.getBean(EurekaInstanceConfigBean.class)
* .getEurekaServerServiceUrls("defaultZone").toString()); }
*/
@Configuration
@EnableConfigurationProperties(EurekaInstanceConfigBean.class)
protected static class TestConfiguration {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment