diff --git a/.gitignore b/.gitignore
index 1311f16e..c55e0766 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,7 +46,8 @@ Thumbs.db
*/target
/build
*/build
-
+/bin
+*/bin
# IntelliJ specific files/directories
out
.idea
diff --git a/karyon-core/build.gradle b/karyon-core/build.gradle
index ec9ff71e..71f200d5 100644
--- a/karyon-core/build.gradle
+++ b/karyon-core/build.gradle
@@ -25,6 +25,7 @@ tasks.withType(Javadoc).each {
dependencies {
dependencies {
compile "com.netflix.rxnetty:rx-netty-contexts:${rxnetty_version}"
+ compile "com.netflix.governator:governator:1.2.20"
}
}
diff --git a/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheck.java b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheck.java
new file mode 100644
index 00000000..b42d8ebc
--- /dev/null
+++ b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheck.java
@@ -0,0 +1,108 @@
+package com.netflix.karyon.health;
+
+/**
+ * SPI for a component implementation an application status condition.
+ * Status conditions can indicate one of three scenarios
+ * 1. Component is not ready (i.e. starting or initializing)
+ * 2. Component is ready and functioning propertly
+ * 3. Component has either failed to start or failed during runtime
+ *
+ * There can be multiple status conditions registered for a single application.
+ * The status conditions are ultimately resolved to a single application up/down
+ * status.
+ *
+ * @author elandau
+ */
+public interface HealthCheck {
+ /**
+ * The status can have one of three states
+ *
+ * 1. Not ready status = false, error = null
+ * 2. Ready status = true, error = null
+ * 3. Error state status = false, error ! =null
+ *
+ * @author elandau
+ *
+ */
+ public static class Status {
+ private final HealthCheck healthCheck;
+ private final Throwable error;
+ private final boolean status;
+
+ private Status(HealthCheck healthCheck, boolean status, Throwable error) {
+ this.status = status;
+ this.error = error;
+ this.healthCheck = healthCheck;
+ }
+
+ /**
+ * Component is ready to be used and functioning properly
+ * @return
+ */
+ public boolean isReady() {
+ return status && error == null;
+ }
+
+ /**
+ * Component is either not ready or failed
+ */
+ public boolean isNotReady() {
+ return !isReady();
+ }
+
+ /**
+ * There was an error starting or running a component
+ */
+ public boolean hasError() {
+ return error != null;
+ }
+
+ public Throwable getError() {
+ return error;
+ }
+
+ /**
+ * @return Name of component(s) being checked by this StatusCheck
+ */
+ public String getName() {
+ return healthCheck.getName();
+ }
+
+ public static Status error(HealthCheck healthCheck, Throwable error) {
+ return new Status(healthCheck, false, error);
+ }
+
+ public static Status ready(HealthCheck healthCheck) {
+ return new Status(healthCheck, true, null);
+ }
+
+ public static Status notReady(HealthCheck healthCheck) {
+ return new Status(healthCheck, false, null);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Status [")
+ .append("healthCheck=").append(healthCheck.getName())
+ .append(", status=") .append(status);
+
+ if (error != null) {
+ sb.append(", error=" + error.getMessage());
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Run the status check and return the current status
+ * @return
+ */
+ public abstract Status check();
+
+ /**
+ * @return Get name for health check
+ */
+ public abstract String getName();
+}
diff --git a/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckHandler.java b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckHandler.java
index e52449a3..96e5233f 100644
--- a/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckHandler.java
+++ b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckHandler.java
@@ -16,6 +16,7 @@
package com.netflix.karyon.health;
+
/**
* This is an extension to the callback handler
* in eureka to provide a
diff --git a/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckInvoker.java b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckInvoker.java
new file mode 100644
index 00000000..ce4d5f74
--- /dev/null
+++ b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckInvoker.java
@@ -0,0 +1,17 @@
+package com.netflix.karyon.health;
+
+import java.util.Collection;
+import java.util.List;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Strategy for invoking the actual healthcheck operations
+ *
+ * @author elandau
+ *
+ */
+@ImplementedBy(InlineHealthCheckInvoker.class)
+public interface HealthCheckInvoker {
+ public List invoke(Collection healthChecks);
+}
diff --git a/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckRegistry.java b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckRegistry.java
new file mode 100644
index 00000000..ec3cae1e
--- /dev/null
+++ b/karyon-core/src/main/java/com/netflix/karyon/health/HealthCheckRegistry.java
@@ -0,0 +1,23 @@
+package com.netflix.karyon.health;
+
+import java.util.List;
+
+/**
+ * Registry of all status checkers. The actual health check is performed
+ * by an implementation of {@link HealthCheckInvoker}
+ *
+ * @author elandau
+ */
+public interface HealthCheckRegistry {
+ /**
+ * @return Return list of all registered HealthCheck conditions
+ */
+ List getHealthChecks();
+
+ /**
+ * Add a HealthCheck to the registry
+ *
+ * @param handler
+ */
+ void registerHealthCheck(HealthCheck handler);
+}
diff --git a/karyon-core/src/main/java/com/netflix/karyon/health/InlineHealthCheckInvoker.java b/karyon-core/src/main/java/com/netflix/karyon/health/InlineHealthCheckInvoker.java
new file mode 100644
index 00000000..a09cbc27
--- /dev/null
+++ b/karyon-core/src/main/java/com/netflix/karyon/health/InlineHealthCheckInvoker.java
@@ -0,0 +1,26 @@
+package com.netflix.karyon.health;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.inject.Singleton;
+
+import com.google.common.collect.Lists;
+import com.netflix.karyon.health.HealthCheck.Status;
+
+/**
+ * Invoke all health checks in the context of the calling thread
+ * @author elandau
+ *
+ */
+@Singleton
+public class InlineHealthCheckInvoker implements HealthCheckInvoker {
+ @Override
+ public List invoke(Collection healthChecks) {
+ List response = Lists.newArrayList();
+ for (HealthCheck hc : healthChecks) {
+ response.add(hc.check());
+ }
+ return response;
+ }
+}
diff --git a/karyon-core/src/main/java/com/netflix/karyon/health/ManualHealthCheck.java b/karyon-core/src/main/java/com/netflix/karyon/health/ManualHealthCheck.java
new file mode 100644
index 00000000..46542d48
--- /dev/null
+++ b/karyon-core/src/main/java/com/netflix/karyon/health/ManualHealthCheck.java
@@ -0,0 +1,43 @@
+package com.netflix.karyon.health;
+
+/**
+ * Implementation of HealthCheck that may be set manually.
+ *
+ * @author elandau
+ */
+public class ManualHealthCheck implements HealthCheck {
+ private volatile Status status;
+ private final String name;
+
+ public ManualHealthCheck() {
+ this("manual");
+ }
+
+ public ManualHealthCheck(String name) {
+ this.name = name;
+ this.status = Status.error(this, null);
+ }
+
+ public void unhealthy(Throwable error) {
+ this.status = Status.error(this, error);
+ }
+
+ public void healthy() {
+ this.status = Status.ready(this);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Status check() {
+ return status;
+ }
+
+ @Override
+ public String toString() {
+ return "ManualHealthCheck [status=" + status + ", name=" + name + "]";
+ }
+}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/ApplicationInfoManagerHealthCheck.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/ApplicationInfoManagerHealthCheck.java
new file mode 100644
index 00000000..73d64130
--- /dev/null
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/ApplicationInfoManagerHealthCheck.java
@@ -0,0 +1,59 @@
+package com.netflix.karyon.eureka;
+
+import javax.inject.Singleton;
+
+import com.google.inject.Inject;
+import com.netflix.appinfo.ApplicationInfoManager;
+import com.netflix.appinfo.InstanceInfo.InstanceStatus;
+import com.netflix.karyon.health.HealthCheck;
+
+/**
+ * StatusCheck based on the ApplicationInfoManager tracked status.
+ *
+ * @author elandau
+ */
+@Singleton
+public class ApplicationInfoManagerHealthCheck implements HealthCheck {
+ ApplicationInfoManager manager;
+
+ @Deprecated
+ public ApplicationInfoManagerHealthCheck() {
+ manager = ApplicationInfoManager.getInstance();
+ }
+
+ @Inject
+ public ApplicationInfoManagerHealthCheck(ApplicationInfoManager manager) {
+ this.manager = manager;
+ }
+
+ @Override
+ public Status check() {
+ InstanceStatus status = manager.getInstanceStatus();
+ if (status != null) {
+ switch (status) {
+ case UP:
+ return Status.ready(this);
+ case STARTING:
+ return Status.error(this, null);
+ case DOWN:
+ return Status.error(this, new Exception("Application is DOWN"));
+ default:
+ return Status.error(this, new Exception("Invalid state : " + status));
+ }
+ }
+ else {
+ return Status.error(this, new Exception("Invalid status "));
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "ApplicationInfoManager";
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationInfoManagerHealthCheck []";
+ }
+
+}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultEurekaKaryonStatusBridge.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultEurekaKaryonStatusBridge.java
index b7794a48..17944911 100644
--- a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultEurekaKaryonStatusBridge.java
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultEurekaKaryonStatusBridge.java
@@ -5,6 +5,7 @@
/**
* @author Nitesh Kant
*/
+@Deprecated
public class DefaultEurekaKaryonStatusBridge implements EurekaKaryonStatusBridge {
@Override
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultHealthCheckRegistry.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultHealthCheckRegistry.java
new file mode 100644
index 00000000..de8566b4
--- /dev/null
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/DefaultHealthCheckRegistry.java
@@ -0,0 +1,82 @@
+package com.netflix.karyon.eureka;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import com.google.inject.Inject;
+import com.google.inject.util.Providers;
+import com.netflix.karyon.health.HealthCheckHandler;
+import com.netflix.karyon.health.HealthCheck;
+import com.netflix.karyon.health.HealthCheckRegistry;
+
+/**
+ * Simple health check registry that supports the following HealthChecks
+ * 1. Bridge for ApplicationInfoManager's manual HealthCheck status
+ * 2. Bridge for HealthCheckHandler
+ * 3. Set creating using guice's multibinding
+ *
+ * Note that all healthchecks are injected lazily using Providers to ensure there is no
+ * circular dependency for components that depend on DiscoveryClient.
+ *
+ * @author elandau
+ */
+@Singleton
+public class DefaultHealthCheckRegistry implements HealthCheckRegistry {
+ private final CopyOnWriteArrayList> healthChecks = new CopyOnWriteArrayList>();
+
+ public static class OptionalArgs {
+ @Inject(optional=true)
+ Provider handler;
+
+ @Inject(optional=true)
+ Set> healthChecks;
+ }
+
+ @Inject
+ DefaultHealthCheckRegistry(Provider manager, OptionalArgs args) {
+ this(args.healthChecks, manager, args.handler);
+ }
+
+ public DefaultHealthCheckRegistry(
+ final Set> healthChecks,
+ final Provider manager,
+ final Provider handler) {
+
+ if (manager != null) {
+ this.healthChecks.add(manager);
+ }
+
+ if (handler != null) {
+ this.healthChecks.add(Providers.of(new HealthCheckHandlerToHealthCheckAdapter(handler, "legacy")));
+ }
+
+ if (healthChecks != null) {
+ this.healthChecks.addAll(healthChecks);
+ }
+ }
+
+ /**
+ * Return a list of ALL registered handlers
+ */
+ @Override
+ public List getHealthChecks() {
+ List statuses = new ArrayList();
+ for (Provider extends HealthCheck> provider : healthChecks) {
+ HealthCheck hc = provider.get();
+ if (hc != null) {
+ statuses.add(provider.get());
+ }
+ }
+ return statuses;
+ }
+
+ @Override
+ public void registerHealthCheck(HealthCheck handler) {
+ this.healthChecks.add(Providers.of(handler));
+ }
+}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckHandler.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckHandler.java
index 3bf4a163..0fb56756 100644
--- a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckHandler.java
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckHandler.java
@@ -16,31 +16,30 @@
package com.netflix.karyon.eureka;
-import com.netflix.appinfo.InstanceInfo;
-import com.netflix.karyon.health.HealthCheckHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import javax.inject.Inject;
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.karyon.health.HealthCheckInvoker;
+import com.netflix.karyon.health.HealthCheckRegistry;
+
/**
* @author Nitesh Kant
*/
public class EurekaHealthCheckHandler implements com.netflix.appinfo.HealthCheckHandler {
- private final HealthCheckHandler healthCheckHandler;
- private final EurekaKaryonStatusBridge eurekaKaryonStatusBridge;
+ private final HealthCheckRegistry registry;
+ private HealthCheckInvoker invoker;
+ private EurekaHealthCheckResolver resolver;
@Inject
- public EurekaHealthCheckHandler(HealthCheckHandler healthCheckHandler,
- EurekaKaryonStatusBridge eurekaKaryonStatusBridge) {
- this.healthCheckHandler = healthCheckHandler;
- this.eurekaKaryonStatusBridge = eurekaKaryonStatusBridge;
+ public EurekaHealthCheckHandler(HealthCheckRegistry registry, HealthCheckInvoker invoker, EurekaHealthCheckResolver resolver) {
+ this.registry = registry;
+ this.invoker = invoker;
+ this.resolver = resolver;
}
@Override
public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) {
- int healthStatus = healthCheckHandler.getStatus();
- return eurekaKaryonStatusBridge.interpretKaryonStatus(healthStatus);
+ return resolver.resolve(invoker.invoke(registry.getHealthChecks()));
}
}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckResolver.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckResolver.java
new file mode 100644
index 00000000..37ba09cd
--- /dev/null
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaHealthCheckResolver.java
@@ -0,0 +1,17 @@
+package com.netflix.karyon.eureka;
+
+import java.util.Collection;
+
+import com.google.inject.ImplementedBy;
+import com.netflix.appinfo.InstanceInfo.InstanceStatus;
+import com.netflix.karyon.health.HealthCheck;
+
+/**
+ * SPI for strategy to resolve a set of {@link HealthCheck.Status}'s into a single
+ * {@link HealthCheck.Status}
+ * @author elandau
+ */
+@ImplementedBy(WorstStatusEurekaHealthCheckResolver.class)
+public interface EurekaHealthCheckResolver {
+ InstanceStatus resolve(Collection statuses);
+}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaKaryonStatusBridge.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaKaryonStatusBridge.java
index f6fda2a6..836ad669 100644
--- a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaKaryonStatusBridge.java
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/EurekaKaryonStatusBridge.java
@@ -6,6 +6,7 @@
/**
* @author Nitesh Kant
*/
+@Deprecated
@ImplementedBy(DefaultEurekaKaryonStatusBridge.class)
public interface EurekaKaryonStatusBridge {
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/HealthCheckCallbackToHealthCheckAdapter.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/HealthCheckCallbackToHealthCheckAdapter.java
new file mode 100644
index 00000000..aad11538
--- /dev/null
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/HealthCheckCallbackToHealthCheckAdapter.java
@@ -0,0 +1,37 @@
+package com.netflix.karyon.eureka;
+
+import com.netflix.appinfo.HealthCheckCallback;
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.karyon.health.HealthCheck;
+
+public class HealthCheckCallbackToHealthCheckAdapter implements HealthCheck {
+ private final HealthCheckCallback callback;
+ private final InstanceInfo instanceInfo;
+ private final String name;
+
+ public HealthCheckCallbackToHealthCheckAdapter(HealthCheckCallback callback, InstanceInfo instanceInfo) {
+ this.callback = callback;
+ this.instanceInfo = instanceInfo;
+ this.name = callback.getClass().getName();
+ }
+
+ @Override
+ public Status check() {
+ try {
+ if (callback.isHealthy()) {
+ return Status.ready(this);
+ }
+ else {
+ return Status.notReady(this);
+ }
+ }
+ catch (Exception e) {
+ return Status.error(this, e);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/HealthCheckHandlerToHealthCheckAdapter.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/HealthCheckHandlerToHealthCheckAdapter.java
new file mode 100644
index 00000000..88329122
--- /dev/null
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/HealthCheckHandlerToHealthCheckAdapter.java
@@ -0,0 +1,52 @@
+package com.netflix.karyon.eureka;
+
+import javax.inject.Provider;
+
+import com.google.inject.util.Providers;
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.karyon.health.HealthCheckHandler;
+import com.netflix.karyon.health.HealthCheck;
+
+/**
+ * Adapter to convert a HealthCheckHandler into a HealthCheck.Status
+ *
+ * @author elandau
+ */
+public class HealthCheckHandlerToHealthCheckAdapter implements HealthCheck {
+ private final Provider handler;
+ private final String name;
+
+ public HealthCheckHandlerToHealthCheckAdapter(Provider handler, String name) {
+ this.handler = handler;
+ this.name = name;
+ }
+
+ public HealthCheckHandlerToHealthCheckAdapter(HealthCheckHandler handler, InstanceInfo instanceInfo) {
+ this.handler = Providers.of(handler);
+ this.name = handler.getClass().getName();
+ }
+
+ @Override
+ public Status check() {
+ try {
+ int status = handler.get().getStatus();
+ if (status == 204) {
+ return Status.notReady(this);
+ }
+ else if (status >= 200 || status <= 300) {
+ return Status.ready(this);
+ }
+ else {
+ return Status.error(this, new Exception("Bad status " + status));
+ }
+ }
+ catch (Exception e) {
+ return Status.error(this, e);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/KaryonEurekaModule.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/KaryonEurekaModule.java
index ce172353..ff372a43 100644
--- a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/KaryonEurekaModule.java
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/KaryonEurekaModule.java
@@ -11,6 +11,7 @@
import com.netflix.discovery.providers.DefaultEurekaClientConfigProvider;
import com.netflix.governator.guice.LifecycleInjectorBuilder;
import com.netflix.governator.guice.LifecycleInjectorBuilderSuite;
+import com.netflix.karyon.health.HealthCheckRegistry;
/**
* @author Nitesh Kant
@@ -22,6 +23,7 @@ protected void configure() {
bind(com.netflix.appinfo.HealthCheckHandler.class).to(EurekaHealthCheckHandler.class);
bind(ApplicationInfoManager.class).asEagerSingleton();
bind(DiscoveryClient.class).asEagerSingleton();
+ bind(HealthCheckRegistry.class).to(DefaultHealthCheckRegistry.class);
configureEureka();
}
diff --git a/karyon-eureka/src/main/java/com/netflix/karyon/eureka/WorstStatusEurekaHealthCheckResolver.java b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/WorstStatusEurekaHealthCheckResolver.java
new file mode 100644
index 00000000..47a79dcb
--- /dev/null
+++ b/karyon-eureka/src/main/java/com/netflix/karyon/eureka/WorstStatusEurekaHealthCheckResolver.java
@@ -0,0 +1,35 @@
+package com.netflix.karyon.eureka;
+
+import java.util.Collection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.netflix.appinfo.InstanceInfo.InstanceStatus;
+import com.netflix.karyon.health.HealthCheck;
+
+/**
+ * Specialized HealthCheckResolver that will return the first occurance of DOWN
+ * or STARTING (in that order) or UP if all status checks are ready.
+ * @author elandau
+ *
+ */
+public class WorstStatusEurekaHealthCheckResolver implements EurekaHealthCheckResolver {
+ private static final Logger LOG = LoggerFactory.getLogger(WorstStatusEurekaHealthCheckResolver.class);
+
+ @Override
+ public InstanceStatus resolve(Collection statuses) {
+ if (statuses != null) {
+ for (HealthCheck.Status status : statuses) {
+ LOG.info("Status of '{}' : {} ({})", status.getName(), status.isReady(), status.getError());
+ if (status.hasError()) {
+ return InstanceStatus.DOWN;
+ }
+ if (!status.isReady()) {
+ return InstanceStatus.STARTING;
+ }
+ }
+ }
+ return InstanceStatus.UP;
+ }
+}
diff --git a/karyon-eureka/src/test/java/com/netflix/karyon/eureka/HealthCheckRegistryTest.java b/karyon-eureka/src/test/java/com/netflix/karyon/eureka/HealthCheckRegistryTest.java
new file mode 100644
index 00000000..18454702
--- /dev/null
+++ b/karyon-eureka/src/test/java/com/netflix/karyon/eureka/HealthCheckRegistryTest.java
@@ -0,0 +1,152 @@
+package com.netflix.karyon.eureka;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.inject.Singleton;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Injector;
+import com.netflix.appinfo.ApplicationInfoManager;
+import com.netflix.appinfo.DataCenterInfo;
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.appinfo.InstanceInfo.InstanceStatus;
+import com.netflix.config.ConfigurationManager;
+import com.netflix.discovery.MockRemoteEurekaServer;
+import com.netflix.governator.guice.LifecycleInjector;
+import com.netflix.governator.guice.LifecycleInjectorMode;
+import com.netflix.karyon.health.HealthCheckHandler;
+import com.netflix.karyon.health.HealthCheckInvoker;
+import com.netflix.karyon.health.HealthCheckRegistry;
+
+public class HealthCheckRegistryTest {
+ public static final String REMOTE_REGION = "myregion";
+ public static final String REMOTE_ZONE = "myzone";
+
+ @Singleton
+ public static class TestModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ ConfigurationManager.getConfigInstance().setProperty("eureka.shouldFetchRegistry", "true");
+ ConfigurationManager.getConfigInstance().setProperty("eureka.responseCacheAutoExpirationInSeconds", "10");
+ ConfigurationManager.getConfigInstance().setProperty("eureka.client.refresh.interval", "60");
+ ConfigurationManager.getConfigInstance().setProperty("eureka.registration.enabled", "false");
+ ConfigurationManager.getConfigInstance().setProperty("eureka.validateInstanceId", "false");
+ ConfigurationManager.getConfigInstance().setProperty("eureka.fetchRemoteRegionsRegistry", REMOTE_REGION);
+ ConfigurationManager.getConfigInstance().setProperty("eureka.myregion.availabilityZones", REMOTE_ZONE);
+ ConfigurationManager.getConfigInstance().setProperty("eureka.serviceUrl.default",
+ "http://localhost:" + 7777 +
+ MockRemoteEurekaServer.EUREKA_API_BASE_PATH);
+
+ DataCenterInfo myDCI = new DataCenterInfo() {
+ public DataCenterInfo.Name getName() { return DataCenterInfo.Name.MyOwn; }
+ };
+
+ bind(HealthCheckRegistry.class).to(DefaultHealthCheckRegistry.class);
+ bind(com.netflix.appinfo.HealthCheckHandler.class).to(EurekaHealthCheckHandler.class);
+ bind(InstanceInfo.class).toInstance(
+ InstanceInfo.Builder.newBuilder()
+ .setAppName("test")
+ .setStatus(InstanceStatus.STARTING)
+ .setDataCenterInfo(myDCI).build());
+ }
+ }
+
+ @Test
+ public void defaultShouldBeStarting() {
+ Injector injector = LifecycleInjector.builder()
+ .withModuleClasses(TestModule.class)
+ .build()
+ .createInjector();
+ HealthCheckRegistry registry = injector.getInstance(HealthCheckRegistry.class);
+ com.netflix.appinfo.HealthCheckHandler handler = injector.getInstance(com.netflix.appinfo.HealthCheckHandler.class);
+
+ Assert.assertEquals(1, registry.getHealthChecks().size());
+
+ Assert.assertEquals(InstanceStatus.STARTING, handler.getStatus(null));
+
+ ApplicationInfoManager.getInstance().setInstanceStatus(InstanceStatus.UP);
+ Assert.assertEquals(InstanceStatus.UP, handler.getStatus(null));
+ }
+
+ @Test
+ public void defaultShouldBeStartingWithDefaultHealthCheck() {
+ final AtomicInteger status = new AtomicInteger(204);
+
+ Injector injector = LifecycleInjector.builder()
+ .withMode(LifecycleInjectorMode.SIMULATED_CHILD_INJECTORS)
+ .withModuleClasses(TestModule.class)
+ .withAdditionalModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(HealthCheckHandler.class).toInstance(new HealthCheckHandler() {
+ @Override
+ public int getStatus() {
+ return status.get();
+ }
+ });
+ }
+ })
+ .build()
+ .createInjector();
+ HealthCheckHandler hcHandler = injector.getInstance(HealthCheckHandler.class);
+ HealthCheckRegistry registry = injector.getInstance(HealthCheckRegistry.class);
+ HealthCheckInvoker invoker = injector.getInstance(HealthCheckInvoker.class);
+ com.netflix.appinfo.HealthCheckHandler handler = injector.getInstance(com.netflix.appinfo.HealthCheckHandler.class);
+
+ Assert.assertNotNull(hcHandler);
+
+ Assert.assertEquals(2, registry.getHealthChecks().size());
+
+ Assert.assertEquals(InstanceStatus.STARTING, handler.getStatus(null));
+
+ ApplicationInfoManager.getInstance().setInstanceStatus(InstanceStatus.UP);
+ status.set(200);
+
+ Assert.assertEquals(InstanceStatus.UP, handler.getStatus(null));
+
+ status.set(500);
+
+ Assert.assertEquals(InstanceStatus.DOWN, handler.getStatus(null));
+
+ System.out.println(registry.getHealthChecks());
+ System.out.println(invoker.invoke(registry.getHealthChecks()));
+ }
+
+ @Test
+ public void reportExceptionForFailingHealthCheck() {
+ Injector injector = LifecycleInjector.builder()
+ .withMode(LifecycleInjectorMode.SIMULATED_CHILD_INJECTORS)
+ .withModuleClasses(TestModule.class)
+ .withAdditionalModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(HealthCheckHandler.class).toInstance(new HealthCheckHandler() {
+ @Override
+ public int getStatus() {
+ throw new RuntimeException("Failed");
+ }
+ });
+ }
+ })
+ .build()
+ .createInjector();
+ HealthCheckHandler hcHandler = injector.getInstance(HealthCheckHandler.class);
+ HealthCheckRegistry registry = injector.getInstance(HealthCheckRegistry.class);
+ HealthCheckInvoker invoker = injector.getInstance(HealthCheckInvoker.class);
+ com.netflix.appinfo.HealthCheckHandler handler = injector.getInstance(com.netflix.appinfo.HealthCheckHandler.class);
+
+ Assert.assertNotNull(hcHandler);
+
+ Assert.assertEquals(2, registry.getHealthChecks().size());
+ ApplicationInfoManager.getInstance().setInstanceStatus(InstanceStatus.UP);
+
+ Assert.assertEquals(InstanceStatus.DOWN, handler.getStatus(null));
+
+ System.out.println(registry.getHealthChecks());
+ System.out.println(invoker.invoke(registry.getHealthChecks()));
+ }
+}