package com.netflix.discovery;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Scopes;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.PropertiesInstanceConfig;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import com.netflix.discovery.shared.transport.SimpleEurekaHttpServer;
import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs;
import com.netflix.discovery.util.InstanceInfoGenerator;
import com.netflix.governator.guice.LifecycleInjector;
import com.netflix.governator.lifecycle.LifecycleManager;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse;
import static com.netflix.discovery.util.EurekaEntityFunctions.countInstances;
import static java.util.Collections.singletonList;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class EurekaClientLifecycleTest {
private static final String MY_APPLICATION_NAME = "MYAPPLICATION";
private static final String MY_INSTANCE_ID = "myInstanceId";
private static final Applications APPLICATIONS = InstanceInfoGenerator.newBuilder(1, 1).build().toApplications();
private static final Applications APPLICATIONS_DELTA = new Applications(APPLICATIONS.getAppsHashCode(), 1L, Collections.<Application>emptyList());
public static final int TIMEOUT_MS = 2 * 1000;
public static final EurekaHttpClient requestHandler = mock(EurekaHttpClient.class);
public static SimpleEurekaHttpServer eurekaHttpServer;
@BeforeClass
public static void setupClass() throws IOException {
eurekaHttpServer = new SimpleEurekaHttpServer(requestHandler);
when(requestHandler.register(any(InstanceInfo.class))).thenReturn(EurekaHttpResponse.status(204));
when(requestHandler.cancel(MY_APPLICATION_NAME, MY_INSTANCE_ID)).thenReturn(EurekaHttpResponse.status(200));
when(requestHandler.sendHeartBeat(MY_APPLICATION_NAME, MY_INSTANCE_ID, null, null)).thenReturn(
anEurekaHttpResponse(200, InstanceInfo.class).build()
);
when(requestHandler.getApplications()).thenReturn(
anEurekaHttpResponse(200, APPLICATIONS).type(MediaType.APPLICATION_JSON_TYPE).build()
);
when(requestHandler.getDelta()).thenReturn(
anEurekaHttpResponse(200, APPLICATIONS_DELTA).type(MediaType.APPLICATION_JSON_TYPE).build()
);
}
@AfterClass
public static void tearDownClass() {
if (eurekaHttpServer != null) {
eurekaHttpServer.shutdown();
}
}
@Test
public void testEurekaClientLifecycle() throws Exception {
Injector injector = LifecycleInjector.builder()
.withModules(
new AbstractModule() {
@Override
protected void configure() {
bind(EurekaInstanceConfig.class).to(LocalEurekaInstanceConfig.class);
bind(EurekaClientConfig.class).to(LocalEurekaClientConfig.class);
bind(AbstractDiscoveryClientOptionalArgs.class).to(Jersey1DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON);
}
}
)
.build().createInjector();
LifecycleManager lifecycleManager = injector.getInstance(LifecycleManager.class);
lifecycleManager.start();
EurekaClient client = injector.getInstance(EurekaClient.class);
// Check registration
verify(requestHandler, timeout(TIMEOUT_MS).atLeast(1)).register(any(InstanceInfo.class));
// Check registry fetch
verify(requestHandler, timeout(TIMEOUT_MS).times(1)).getApplications();
verify(requestHandler, timeout(TIMEOUT_MS).atLeast(1)).getDelta();
assertThat(countInstances(client.getApplications()), is(equalTo(1)));
// Shutdown container, and check that unregister happens
lifecycleManager.close();
verify(requestHandler, times(1)).cancel(MY_APPLICATION_NAME, MY_INSTANCE_ID);
}
@Test
public void testBackupRegistryInjection() throws Exception {
final BackupRegistry backupRegistry = mock(BackupRegistry.class);
when(backupRegistry.fetchRegistry()).thenReturn(APPLICATIONS);
Injector injector = LifecycleInjector.builder()
.withModules(
new AbstractModule() {
@Override
protected void configure() {
bind(EurekaInstanceConfig.class).to(LocalEurekaInstanceConfig.class);
bind(EurekaClientConfig.class).to(BadServerEurekaClientConfig.class);
bind(BackupRegistry.class).toInstance(backupRegistry);
bind(AbstractDiscoveryClientOptionalArgs.class).to(Jersey1DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON);
}
}
)
.build().createInjector();
LifecycleManager lifecycleManager = injector.getInstance(LifecycleManager.class);
lifecycleManager.start();
EurekaClient client = injector.getInstance(EurekaClient.class);
verify(backupRegistry, atLeast(1)).fetchRegistry();
assertThat(countInstances(client.getApplications()), is(equalTo(1)));
}
private static class LocalEurekaInstanceConfig extends PropertiesInstanceConfig {
@Override
public String getInstanceId() {
return MY_INSTANCE_ID;
}
@Override
public String getAppname() {
return MY_APPLICATION_NAME;
}
@Override
public int getLeaseRenewalIntervalInSeconds() {
return 1;
}
}
private static class LocalEurekaClientConfig extends DefaultEurekaClientConfig {
@Override
public List<String> getEurekaServerServiceUrls(String myZone) {
return singletonList(eurekaHttpServer.getServiceURI().toString());
}
@Override
public int getInitialInstanceInfoReplicationIntervalSeconds() {
return 0;
}
@Override
public int getInstanceInfoReplicationIntervalSeconds() {
return 1;
}
@Override
public int getRegistryFetchIntervalSeconds() {
return 1;
}
}
private static class BadServerEurekaClientConfig extends LocalEurekaClientConfig {
@Override
public List<String> getEurekaServerServiceUrls(String myZone) {
return singletonList("http://localhost:0/v2/"); // Fail fast on bad port number
}
@Override
public boolean shouldRegisterWithEureka() {
return false;
}
}
}