diff --git a/pom.xml b/pom.xml
index dd8ec313..ce7c0ee4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,7 @@
spring-cloud-netflix-hystrix-dashboard
spring-cloud-netflix-eureka-server
spring-cloud-netflix-turbine
+ spring-cloud-netflix-sidecar
docs
@@ -253,10 +254,21 @@
+
+ 0.6.3
+ 1.1.145
+ 6.1.3
+ 1.4.0-RC5
+ 2.0-RC13
+ 1.0.0
+ 1.0.28
+ 0.20.7
+ 1.7
+
+
- org.apache.maven.plugins
maven-compiler-plugin
3.1
@@ -267,15 +279,4 @@
-
- 0.6.3
- 1.1.145
- 6.1.3
- 1.4.0-RC5
- 2.0-RC13
- 1.0.0
- 1.0.28
- 0.20.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 2cd7da29..084f6f70 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
@@ -13,5 +13,7 @@ public interface DiscoveryClient {
*/
public ServiceInstance getLocalServiceInstance();
+ public List getInstances(String serviceId);
+
public List getServices();
}
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 05aef595..c4508f63 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
@@ -46,6 +46,42 @@ 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();
+ }
+ }
+
@Override
public List getServices() {
Applications applications = discovery.getApplications();
diff --git a/spring-cloud-netflix-sidecar/pom.xml b/spring-cloud-netflix-sidecar/pom.xml
new file mode 100644
index 00000000..ad30ed54
--- /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 00000000..a4400808
--- /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 00000000..a0b5095b
--- /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 00000000..99b48b83
--- /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 00000000..52d59f5a
--- /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 00000000..052c9c98
--- /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 00000000..956559fa
--- /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 00000000..ee3fdf5a
--- /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 00000000..14912394
--- /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
+