diff --git a/pom.xml b/pom.xml
index 1c70ea95a..7255b0b44 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,7 @@
spring-cloud-netflix-eureka-server
spring-cloud-netflix-turbine
spring-cloud-netflix-zuul-server
+ spring-cloud-netflix-sidecar
docs
@@ -263,6 +264,20 @@
1.0.0
1.0.28
0.20.7
+ 1.7
+
+
+
+ maven-compiler-plugin
+ 3.1
+
+ 1.7
+ 1.7
+
+
+
+
+
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java
index b17b3cf57..649dbd44e 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java
@@ -2,6 +2,8 @@ package org.springframework.cloud.client.discovery;
import org.springframework.cloud.client.ServiceInstance;
+import java.util.List;
+
/**
* @author Spencer Gibb
*/
@@ -10,4 +12,6 @@ public interface DiscoveryClient {
* @return ServiceInstance with information used to register the local service
*/
public ServiceInstance getLocalServiceInstance();
+
+ public List getInstances(String serviceId);
}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java
index 308b491bb..e7dc2e330 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java
@@ -1,9 +1,17 @@
package org.springframework.cloud.netflix.eureka;
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import com.netflix.appinfo.InstanceInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
+import javax.annotation.Nullable;
+import java.util.List;
+
+import static com.google.common.collect.Iterables.*;
+
/**
* @author Spencer Gibb
*/
@@ -12,6 +20,9 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
@Autowired
private EurekaInstanceConfigBean config;
+ @Autowired
+ private com.netflix.discovery.DiscoveryClient discovery;
+
@Override
public ServiceInstance getLocalServiceInstance() {
return new ServiceInstance() {
@@ -31,4 +42,40 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
}
};
}
+
+ @Override
+ public List getInstances(String serviceId) {
+ List infos = discovery.getInstancesByVipAddress(serviceId, false);
+ Iterable instances = transform(infos, new Function() {
+ @Nullable
+ @Override
+ public ServiceInstance apply(@Nullable InstanceInfo info) {
+ return new EurekaServiceInstance(info);
+ }
+ });
+ return Lists.newArrayList(instances);
+ }
+
+ static class EurekaServiceInstance implements ServiceInstance {
+ InstanceInfo instance;
+
+ EurekaServiceInstance(InstanceInfo instance) {
+ this.instance = instance;
+ }
+
+ @Override
+ public String getServiceId() {
+ return instance.getAppName();
+ }
+
+ @Override
+ public String getHost() {
+ return instance.getHostName();
+ }
+
+ @Override
+ public int getPort() {
+ return instance.getPort();
+ }
+ }
}
diff --git a/spring-cloud-netflix-sidecar/pom.xml b/spring-cloud-netflix-sidecar/pom.xml
new file mode 100644
index 000000000..ad30ed54f
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+ spring-cloud-netflix-sidecar
+ jar
+ Spring Cloud Netflix Sidecar
+ http://projects.spring.io/spring-cloud/
+
+ org.springframework.cloud
+ spring-cloud-netflix
+ 1.0.0.BUILD-SNAPSHOT
+ ..
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-netflix-core
+
+
+ com.netflix.eureka
+ eureka-client
+
+
+ com.netflix.hystrix
+ hystrix-core
+
+
+ com.netflix.hystrix
+ hystrix-metrics-event-stream
+
+
+ com.netflix.hystrix
+ hystrix-javanica
+
+
+ com.netflix.ribbon
+ ribbon
+
+
+ com.netflix.ribbon
+ ribbon-core
+
+
+ com.netflix.ribbon
+ ribbon-eureka
+
+
+ com.netflix.ribbon
+ ribbon-httpclient
+
+
+ com.netflix.zuul
+ zuul-core
+
+
+ org.projectlombok
+ lombok
+
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
diff --git a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/EnableSidecar.java b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/EnableSidecar.java
new file mode 100644
index 000000000..a44008080
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/EnableSidecar.java
@@ -0,0 +1,21 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.netflix.hystrix.EnableHystrix;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+/**
+ * @author Spencer Gibb
+ */
+@EnableHystrix
+@EnableEurekaClient
+@EnableZuulProxy
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(SidecarConfiguration.class)
+public @interface EnableSidecar {
+}
diff --git a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/LocalApplicationHealthIndicator.java b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/LocalApplicationHealthIndicator.java
new file mode 100644
index 000000000..a0b5095b8
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/LocalApplicationHealthIndicator.java
@@ -0,0 +1,49 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.health.AbstractHealthIndicator;
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * @author Spencer Gibb
+ */
+public class LocalApplicationHealthIndicator extends AbstractHealthIndicator {
+
+ @Autowired
+ SidecarProperties properties;
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void doHealthCheck(Health.Builder builder) throws Exception {
+ URI uri = properties.getLocalHealthUri();
+ if (uri == null) {
+ builder.up();
+ return;
+ }
+
+ Map map = new RestTemplate().getForObject(uri, Map.class);
+ Object status = map.get("status");
+
+ if (status != null && status instanceof String) {
+ builder.status(status.toString());
+ } else if (status != null && status instanceof Map) {
+ Map statusMap = (Map) status;
+ Object code = statusMap.get("code");
+ if (code != null) {
+ builder.status(code.toString());
+ } else {
+ getWarning(builder);
+ }
+ } else {
+ getWarning(builder);
+ }
+ }
+
+ private Health.Builder getWarning(Health.Builder builder) {
+ return builder.unknown().withDetail("warning", "no status field in response");
+ }
+}
diff --git a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java
new file mode 100644
index 000000000..99b48b831
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarConfiguration.java
@@ -0,0 +1,30 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Spencer Gibb
+ */
+@Configuration
+@EnableConfigurationProperties
+@ConditionalOnExpression("${sidecar.enabled:true}")
+public class SidecarConfiguration {
+
+ @Bean
+ public SidecarProperties sidecarProperties() {
+ return new SidecarProperties();
+ }
+
+ @Bean
+ public LocalApplicationHealthIndicator localApplicationHealthIndicator() {
+ return new LocalApplicationHealthIndicator();
+ }
+
+ @Bean
+ public SidecarController sidecarController() {
+ return new SidecarController();
+ }
+}
diff --git a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarController.java b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarController.java
new file mode 100644
index 000000000..52d59f5aa
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarController.java
@@ -0,0 +1,44 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author Spencer Gibb
+ */
+@RestController
+public class SidecarController {
+
+ @Autowired
+ DiscoveryClient discovery;
+
+ @Value("${spring.application.name}")
+ String appName;
+
+ @RequestMapping("/ping")
+ public String ping() {
+ return "OK";
+ }
+
+ @RequestMapping("/hosts")
+ public List hosts(@RequestParam("appName") String appName) {
+ List instances = discovery.getInstances(appName);
+ return instances;
+ }
+
+ @RequestMapping(value = "/", produces = "text/html")
+ public String home() {
+ return "Sidecar\n" +
+ "ping
\n" +
+ "health
\n" +
+ "hosts?appName="+appName+"
\n" +
+ "";
+ }
+}
diff --git a/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarProperties.java b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarProperties.java
new file mode 100644
index 000000000..052c9c987
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/main/java/org/springframework/cloud/netflix/sidecar/SidecarProperties.java
@@ -0,0 +1,15 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.net.URI;
+
+/**
+ * @author Spencer Gibb
+ */
+@Data
+@ConfigurationProperties("sidecar")
+public class SidecarProperties {
+ private URI localHealthUri;
+}
diff --git a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java
new file mode 100644
index 000000000..956559fa9
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplication.java
@@ -0,0 +1,14 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.RestController;
+
+@SpringBootApplication
+@EnableSidecar
+@RestController
+public class SidecarApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(SidecarApplication.class, args);
+ }
+}
diff --git a/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java
new file mode 100644
index 000000000..ee3fdf5a4
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/test/java/org/springframework/cloud/netflix/sidecar/SidecarApplicationTests.java
@@ -0,0 +1,20 @@
+package org.springframework.cloud.netflix.sidecar;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = SidecarApplication.class)
+@WebAppConfiguration
+@IntegrationTest("server.port=0")
+public class SidecarApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/spring-cloud-netflix-sidecar/src/test/resources/application.yml b/spring-cloud-netflix-sidecar/src/test/resources/application.yml
new file mode 100644
index 000000000..14912394b
--- /dev/null
+++ b/spring-cloud-netflix-sidecar/src/test/resources/application.yml
@@ -0,0 +1,15 @@
+server:
+ port: 5678
+spring:
+ application:
+ name: sidecarTest
+
+sidecar:
+ local-health-uri: http://localhost:8081/health
+
+zuul:
+ proxy:
+ route:
+ stores: /stores
+ customers: /customers
+