diff --git a/build.gradle b/build.gradle index 5eb7362..ec520ba 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,8 @@ ext { junitVersion = "4.11" mockitoVersion = "1.9.5" + eurekaClientVersion = "1.1.135" + hadoopCommonVersion = "2.2.0" javadocLinks = [ 'http://docs.oracle.com/javase/7/docs/api/', diff --git a/spring-cloud-pcf-connector/build.gradle b/spring-cloud-pcf-connector/build.gradle index 90c4307..d2f3536 100644 --- a/spring-cloud-pcf-connector/build.gradle +++ b/spring-cloud-pcf-connector/build.gradle @@ -1,9 +1,11 @@ description = 'Spring-Cloud Support for Pivotal CF' dependencies { + compile project(':spring-cloud-core') compile project(':spring-cloud-cloudfoundry-connector') compile project(':spring-cloud-spring-service-connector') - optional("org.apache.hadoop:hadoop-common:2.2.0") - + optional("org.apache.hadoop:hadoop-common:$hadoopCommonVersion") + optional("com.netflix.eureka:eureka-client:$eurekaClientVersion") + testCompile project(path: ':spring-cloud-cloudfoundry-connector', configuration: 'tests') } diff --git a/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaClientConfigurationCreator.java b/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaClientConfigurationCreator.java new file mode 100644 index 0000000..d066eea --- /dev/null +++ b/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaClientConfigurationCreator.java @@ -0,0 +1,191 @@ +package org.springframework.cloud.pcf.eureka; + +import com.netflix.discovery.EurekaClientConfig; +import org.springframework.cloud.service.AbstractServiceConnectorCreator; +import org.springframework.cloud.service.ServiceConnectorConfig; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; + +/** + * + * Connector creator for Eureka client services + * + * @author Chris Schaefer + */ +public class EurekaClientConfigurationCreator extends AbstractServiceConnectorCreator { + @Override + public EurekaClientConfig create(EurekaServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig) { + return getEurekaClientConfig(serviceInfo); + } + + protected EurekaClientConfig getEurekaClientConfig(EurekaServiceInfo serviceInfo) { + return new DefaultPcfEurekaClientConfig(serviceInfo.getUri()); + } + + private static final class DefaultPcfEurekaClientConfig implements EurekaClientConfig { + private static final int MINUTES = 60; + private static final String REGION = "default"; + private static final String DEFAULT_ZONE = "defaultZone"; + private static final String EUREKA_API_PREFIX = "/eureka/"; + + private final String uri; + + public DefaultPcfEurekaClientConfig(String uri) { + this.uri = uri + EUREKA_API_PREFIX; + } + + @Override + public int getRegistryFetchIntervalSeconds() { + return 5; + } + + @Override + public int getInstanceInfoReplicationIntervalSeconds() { + return 30; + } + + @Override + public int getInitialInstanceInfoReplicationIntervalSeconds() { + return 40; + } + + @Override + public int getEurekaServiceUrlPollIntervalSeconds() { + return 5 * MINUTES; + } + + @Override + public String getProxyHost() { + return null; + } + + @Override + public String getProxyPort() { + return null; + } + + @Override + public boolean shouldGZipContent() { + return true; + } + + @Override + public int getEurekaServerReadTimeoutSeconds() { + return 8; + } + + @Override + public int getEurekaServerConnectTimeoutSeconds() { + return 5; + } + + @Override + public String getBackupRegistryImpl() { + return null; + } + + @Override + public int getEurekaServerTotalConnections() { + return 200; + } + + @Override + public int getEurekaServerTotalConnectionsPerHost() { + return 50; + } + + @Override + public String getEurekaServerURLContext() { + return null; + } + + @Override + public String getEurekaServerPort() { + return null; + } + + @Override + public String getEurekaServerDNSName() { + return null; + } + + @Override + public boolean shouldUseDnsForFetchingServiceUrls() { + return false; + } + + @Override + public boolean shouldRegisterWithEureka() { + return true; + } + + @Override + public boolean shouldPreferSameZoneEureka() { + return true; + } + + @Override + public boolean shouldLogDeltaDiff() { + return false; + } + + @Override + public boolean shouldDisableDelta() { + return false; + } + + @Nullable + @Override + public String fetchRegistryForRemoteRegions() { + return null; + } + + @Override + public String getRegion() { + return REGION; + } + + @Override + public String[] getAvailabilityZones(String region) { + return new String[] { DEFAULT_ZONE }; + } + + @Override + public List getEurekaServerServiceUrls(String myZone) { + return Arrays.asList(uri); + } + + @Override + public boolean shouldFilterOnlyUpInstances() { + return true; + } + + @Override + public int getEurekaConnectionIdleTimeoutSeconds() { + return 30; + } + + @Override + public boolean shouldFetchRegistry() { + return true; + } + + @Nullable + @Override + public String getRegistryRefreshSingleVipAddress() { + return null; + } + + @Override + public int getHeartbeatExecutorThreadPoolSize() { + return 2; + } + + @Override + public int getCacheRefreshExecutorThreadPoolSize() { + return 2; + } + } +} diff --git a/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfo.java b/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfo.java new file mode 100644 index 0000000..790c8d5 --- /dev/null +++ b/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfo.java @@ -0,0 +1,16 @@ +package org.springframework.cloud.pcf.eureka; + +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.UriBasedServiceInfo; + +/** + * Information to access Eureka services + * + * @author Chris Schaefer + */ +@ServiceInfo.ServiceLabel("eureka") +public class EurekaServiceInfo extends UriBasedServiceInfo { + public EurekaServiceInfo(String id, String uriString) { + super(id, uriString); + } +} diff --git a/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfoCreator.java b/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfoCreator.java new file mode 100644 index 0000000..c9321a6 --- /dev/null +++ b/spring-cloud-pcf-connector/src/main/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfoCreator.java @@ -0,0 +1,29 @@ +package org.springframework.cloud.pcf.eureka; + +import org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator; +import org.springframework.cloud.cloudfoundry.Tags; + +import java.util.Map; + +/** + * + * Service info creator for Eureka services + * + * @author Chris Schaefer + */ +public class EurekaServiceInfoCreator extends CloudFoundryServiceInfoCreator { + private static final String CREDENTIALS_ID_KEY = "name"; + private static final String EUREKA_SERVICE_TAG_NAME = "eureka"; + + public EurekaServiceInfoCreator() { + super(new Tags(EUREKA_SERVICE_TAG_NAME)); + } + + @Override + public EurekaServiceInfo createServiceInfo(Map serviceData) { + String id = (String) serviceData.get(CREDENTIALS_ID_KEY); + String uri = getUriFromCredentials(getCredentials(serviceData)); + + return new EurekaServiceInfo(id, uri); + } +} diff --git a/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator b/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator index 304d95f..1486458 100644 --- a/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator +++ b/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator @@ -1 +1,2 @@ org.springframework.cloud.pcf.phd.PhdServiceInfoCreator +org.springframework.cloud.pcf.eureka.EurekaServiceInfoCreator diff --git a/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator b/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator index e99f33c..3840a00 100644 --- a/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator +++ b/spring-cloud-pcf-connector/src/main/resources/META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator @@ -1,2 +1,3 @@ org.springframework.cloud.pcf.hadoop.HadoopConfigurationCreator -org.springframework.cloud.pcf.gemfire.GemfireXDDataSourceCreator \ No newline at end of file +org.springframework.cloud.pcf.gemfire.GemfireXDDataSourceCreator +org.springframework.cloud.pcf.eureka.EurekaClientConfigurationCreator diff --git a/spring-cloud-pcf-connector/src/test/java/org/springframework/cloud/pcf/eureka/EurekaClientConfigurationCreatorTest.java b/spring-cloud-pcf-connector/src/test/java/org/springframework/cloud/pcf/eureka/EurekaClientConfigurationCreatorTest.java new file mode 100644 index 0000000..2e3a4dd --- /dev/null +++ b/spring-cloud-pcf-connector/src/test/java/org/springframework/cloud/pcf/eureka/EurekaClientConfigurationCreatorTest.java @@ -0,0 +1,42 @@ +package org.springframework.cloud.pcf.eureka; + +import com.netflix.discovery.EurekaClientConfig; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Test cases around the Eureka client configuration creator + * + * @author Chris Schaefer + */ +public class EurekaClientConfigurationCreatorTest { + private static final String REGION = "default"; + private static final String SERVICE_INFO_ID = "id"; + private static final int REGISTRY_FETCH_INTERVAL_SECS = 5; + private static final String AVAILABLITY_ZONE = "defaultZone"; + private static final String URI = "http://user:pass@192.168.23.4:1234"; + private static final String EUREKA_API_PREFIX = "/eureka/"; + + private EurekaClientConfigurationCreator eurekaClientConfigurationCreator = new EurekaClientConfigurationCreator(); + + @Test + public void testClientConfiguration() { + EurekaServiceInfo eurekaServiceInfo = new EurekaServiceInfo(SERVICE_INFO_ID, URI); + + EurekaClientConfig eurekaClientConfig = eurekaClientConfigurationCreator.create(eurekaServiceInfo, null); + List serviceUrls = eurekaClientConfig.getEurekaServerServiceUrls(null); + + assertEquals(1, serviceUrls.size()); + assertEquals(URI + EUREKA_API_PREFIX, serviceUrls.get(0)); + assertEquals(REGION, eurekaClientConfig.getRegion()); + assertEquals(REGISTRY_FETCH_INTERVAL_SECS, eurekaClientConfig.getRegistryFetchIntervalSeconds()); + + String[] availablityZones = eurekaClientConfig.getAvailabilityZones(null); + + assertEquals(1, availablityZones.length); + assertEquals(AVAILABLITY_ZONE, availablityZones[0]); + } +} diff --git a/spring-cloud-pcf-connector/src/test/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfoCreatorTest.java b/spring-cloud-pcf-connector/src/test/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfoCreatorTest.java new file mode 100644 index 0000000..43ba2ff --- /dev/null +++ b/spring-cloud-pcf-connector/src/test/java/org/springframework/cloud/pcf/eureka/EurekaServiceInfoCreatorTest.java @@ -0,0 +1,45 @@ +package org.springframework.cloud.pcf.eureka; + +import org.junit.Test; +import org.springframework.cloud.cloudfoundry.AbstractCloudFoundryConnectorTest; +import org.springframework.cloud.service.ServiceInfo; + +import java.util.List; + +import static org.mockito.Mockito.when; + +/** + * Connector tests for Eureka services + * + * @author Chris Schaefer + */ +public class EurekaServiceInfoCreatorTest extends AbstractCloudFoundryConnectorTest { + private static final String EUREKA_SERVICE_TAG_NAME = "myEurekaInstance"; + private static final String VCAP_SERVICES_ENV_KEY = "VCAP_SERVICES"; + private static final String PAYLOAD_FILE_NAME = "test-eureka-info.json"; + private static final String PAYLOAD_TEMPLATE_SERVICE_NAME = "$serviceName"; + private static final String PAYLOAD_TEMPLATE_HOSTNAME = "$hostname"; + private static final String PAYLOAD_TEMPLATE_PORT = "$port"; + private static final String PAYLOAD_TEMPLATE_USER = "$user"; + private static final String PAYLOAD_TEMPLATE_PASS = "$pass"; + + @Test + public void eurekaServiceCreationWithTags() { + when(mockEnvironment.getEnvValue(VCAP_SERVICES_ENV_KEY)) + .thenReturn(getServicesPayload(getEurekaServicePayload(EUREKA_SERVICE_TAG_NAME, hostname, port, username, password))); + + List serviceInfos = testCloudConnector.getServiceInfos(); + assertServiceFoundOfType(serviceInfos, EUREKA_SERVICE_TAG_NAME, EurekaServiceInfo.class); + } + + private String getEurekaServicePayload(String serviceName, String hostname, int port, String user, String password) { + String payload = readTestDataFile(PAYLOAD_FILE_NAME); + payload = payload.replace(PAYLOAD_TEMPLATE_SERVICE_NAME, serviceName); + payload = payload.replace(PAYLOAD_TEMPLATE_HOSTNAME, hostname); + payload = payload.replace(PAYLOAD_TEMPLATE_PORT, Integer.toString(port)); + payload = payload.replace(PAYLOAD_TEMPLATE_USER, user); + payload = payload.replace(PAYLOAD_TEMPLATE_PASS, password); + + return payload; + } +} diff --git a/spring-cloud-pcf-connector/src/test/resources/org/springframework/cloud/pcf/eureka/test-eureka-info.json b/spring-cloud-pcf-connector/src/test/resources/org/springframework/cloud/pcf/eureka/test-eureka-info.json new file mode 100644 index 0000000..527deb4 --- /dev/null +++ b/spring-cloud-pcf-connector/src/test/resources/org/springframework/cloud/pcf/eureka/test-eureka-info.json @@ -0,0 +1,11 @@ +{ + "name":"$serviceName", + "label":"p-eureka", + "plan":"standard", + "tags":[ + "eureka" + ], + "credentials":{ + "uri":"http://$username:$password@$hostname:$port/" + } +}