package com.netflix.discovery; import javax.ws.rs.core.MediaType; import java.io.IOException; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import com.netflix.appinfo.DataCenterInfo; import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.junit.resource.DiscoveryClientResource; 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.util.InstanceInfoGenerator; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse; import static com.netflix.discovery.util.EurekaEntityFunctions.copyApplications; import static com.netflix.discovery.util.EurekaEntityFunctions.countInstances; import static com.netflix.discovery.util.EurekaEntityFunctions.mergeApplications; import static com.netflix.discovery.util.EurekaEntityFunctions.takeFirst; import static com.netflix.discovery.util.EurekaEntityFunctions.toApplications; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * @author Nitesh Kant */ public class DiscoveryClientRegistryTest { private static final String TEST_LOCAL_REGION = "us-east-1"; private static final String TEST_REMOTE_REGION = "us-west-2"; private static final String TEST_REMOTE_ZONE = "us-west-2c"; private static final EurekaHttpClient requestHandler = mock(EurekaHttpClient.class); private static SimpleEurekaHttpServer eurekaHttpServer; @Rule public DiscoveryClientResource discoveryClientResource = DiscoveryClientResource.newBuilder() .withRegistration(false) .withRegistryFetch(true) .withRemoteRegions(TEST_REMOTE_REGION) .connectWith(eurekaHttpServer) .build(); /** * Share server stub by all tests. */ @BeforeClass public static void setUpClass() throws IOException { eurekaHttpServer = new SimpleEurekaHttpServer(requestHandler); } @AfterClass public static void tearDownClass() throws Exception { if (eurekaHttpServer != null) { eurekaHttpServer.shutdown(); } } @Before public void setUp() throws Exception { reset(requestHandler); when(requestHandler.cancel(anyString(), anyString())).thenReturn(EurekaHttpResponse.status(200)); when(requestHandler.getDelta()).thenReturn( anEurekaHttpResponse(200, new Applications()).type(MediaType.APPLICATION_JSON_TYPE).build() ); } @Test public void testGetByVipInLocalRegion() throws Exception { Applications applications = InstanceInfoGenerator.newBuilder(4, "app1", "app2").build().toApplications(); InstanceInfo instance = applications.getRegisteredApplications("app1").getInstances().get(0); when(requestHandler.getApplications(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, applications).type(MediaType.APPLICATION_JSON_TYPE).build() ); List<InstanceInfo> result = discoveryClientResource.getClient().getInstancesByVipAddress(instance.getVIPAddress(), false); assertThat(result.size(), is(equalTo(2))); assertThat(result.get(0).getVIPAddress(), is(equalTo(instance.getVIPAddress()))); } @Test public void testGetAllKnownRegions() throws Exception { prepareRemoteRegionRegistry(); EurekaClient client = discoveryClientResource.getClient(); Set<String> allKnownRegions = client.getAllKnownRegions(); assertThat(allKnownRegions.size(), is(equalTo(2))); assertThat(allKnownRegions, hasItem(TEST_REMOTE_REGION)); } @Test public void testAllAppsForRegions() throws Exception { prepareRemoteRegionRegistry(); EurekaClient client = discoveryClientResource.getClient(); Applications appsForRemoteRegion = client.getApplicationsForARegion(TEST_REMOTE_REGION); assertThat(countInstances(appsForRemoteRegion), is(equalTo(4))); Applications appsForLocalRegion = client.getApplicationsForARegion(TEST_LOCAL_REGION); assertThat(countInstances(appsForLocalRegion), is(equalTo(4))); } @Test public void testCacheRefreshSingleAppForLocalRegion() throws Exception { InstanceInfoGenerator instanceGen = InstanceInfoGenerator.newBuilder(2, "testApp").build(); Applications initialApps = instanceGen.takeDelta(1); String vipAddress = initialApps.getRegisteredApplications().get(0).getInstances().get(0).getVIPAddress(); DiscoveryClientResource vipClientResource = discoveryClientResource.fork().withVipFetch(vipAddress).build(); // Take first portion when(requestHandler.getVip(vipAddress, TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, initialApps).type(MediaType.APPLICATION_JSON_TYPE).build() ); EurekaClient vipClient = vipClientResource.getClient(); assertThat(countInstances(vipClient.getApplications()), is(equalTo(1))); // Now second one when(requestHandler.getVip(vipAddress, TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, instanceGen.toApplications()).type(MediaType.APPLICATION_JSON_TYPE).build() ); assertThat(vipClientResource.awaitCacheUpdate(5, TimeUnit.SECONDS), is(true)); assertThat(countInstances(vipClient.getApplications()), is(equalTo(2))); } @Test public void testEurekaClientPeriodicHeartbeat() throws Exception { DiscoveryClientResource registeringClientResource = discoveryClientResource.fork().withRegistration(true).withRegistryFetch(false).build(); InstanceInfo instance = registeringClientResource.getMyInstanceInfo(); when(requestHandler.register(any(InstanceInfo.class))).thenReturn(EurekaHttpResponse.status(204)); when(requestHandler.sendHeartBeat(instance.getAppName(), instance.getId(), null, null)).thenReturn(anEurekaHttpResponse(200, InstanceInfo.class).build()); registeringClientResource.getClient(); // Initialize verify(requestHandler, timeout(5 * 1000).atLeast(2)).sendHeartBeat(instance.getAppName(), instance.getId(), null, null); } @Test public void testEurekaClientPeriodicCacheRefresh() throws Exception { InstanceInfoGenerator instanceGen = InstanceInfoGenerator.newBuilder(3, 1).build(); Applications initialApps = instanceGen.takeDelta(1); // Full fetch when(requestHandler.getApplications(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, initialApps).type(MediaType.APPLICATION_JSON_TYPE).build() ); EurekaClient client = discoveryClientResource.getClient(); assertThat(countInstances(client.getApplications()), is(equalTo(1))); // Delta 1 when(requestHandler.getDelta(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, instanceGen.takeDelta(1)).type(MediaType.APPLICATION_JSON_TYPE).build() ); assertThat(discoveryClientResource.awaitCacheUpdate(5, TimeUnit.SECONDS), is(true)); // Delta 2 when(requestHandler.getDelta(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, instanceGen.takeDelta(1)).type(MediaType.APPLICATION_JSON_TYPE).build() ); assertThat(discoveryClientResource.awaitCacheUpdate(5, TimeUnit.SECONDS), is(true)); assertThat(countInstances(client.getApplications()), is(equalTo(3))); } @Test public void testGetInvalidVIP() throws Exception { Applications applications = InstanceInfoGenerator.newBuilder(1, "testApp").build().toApplications(); when(requestHandler.getApplications(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, applications).type(MediaType.APPLICATION_JSON_TYPE).build() ); EurekaClient client = discoveryClientResource.getClient(); assertThat(countInstances(client.getApplications()), is(equalTo(1))); List<InstanceInfo> instancesByVipAddress = client.getInstancesByVipAddress("XYZ", false); assertThat(instancesByVipAddress.isEmpty(), is(true)); } @Test public void testGetInvalidVIPForRemoteRegion() throws Exception { prepareRemoteRegionRegistry(); EurekaClient client = discoveryClientResource.getClient(); List<InstanceInfo> instancesByVipAddress = client.getInstancesByVipAddress("XYZ", false, TEST_REMOTE_REGION); assertThat(instancesByVipAddress.isEmpty(), is(true)); } @Test public void testGetByVipInRemoteRegion() throws Exception { prepareRemoteRegionRegistry(); EurekaClient client = discoveryClientResource.getClient(); String vipAddress = takeFirst(client.getApplicationsForARegion(TEST_REMOTE_REGION)).getVIPAddress(); List<InstanceInfo> instancesByVipAddress = client.getInstancesByVipAddress(vipAddress, false, TEST_REMOTE_REGION); assertThat(instancesByVipAddress.size(), is(equalTo(2))); InstanceInfo instance = instancesByVipAddress.iterator().next(); assertThat(instance.getVIPAddress(), is(equalTo(vipAddress))); } @Test public void testAppsHashCodeAfterRefresh() throws Exception { InstanceInfoGenerator instanceGen = InstanceInfoGenerator.newBuilder(2, "testApp").build(); // Full fetch with one item InstanceInfo first = instanceGen.first(); Applications initial = toApplications(first); when(requestHandler.getApplications(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, initial).type(MediaType.APPLICATION_JSON_TYPE).build() ); EurekaClient client = discoveryClientResource.getClient(); assertThat(client.getApplications().getAppsHashCode(), is(equalTo("UP_1_"))); // Delta with one add InstanceInfo second = new InstanceInfo.Builder(instanceGen.take(1)).setStatus(InstanceStatus.DOWN).build(); Applications delta = toApplications(second); delta.setAppsHashCode("DOWN_1_UP_1_"); when(requestHandler.getDelta(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, delta).type(MediaType.APPLICATION_JSON_TYPE).build() ); assertThat(discoveryClientResource.awaitCacheUpdate(5, TimeUnit.SECONDS), is(true)); assertThat(client.getApplications().getAppsHashCode(), is(equalTo("DOWN_1_UP_1_"))); } @Test public void testApplyDeltaWithBadInstanceInfoDataCenterInfoAsNull() throws Exception { InstanceInfoGenerator instanceGen = InstanceInfoGenerator.newBuilder(2, "testApp").build(); // Full fetch with one item InstanceInfo first = instanceGen.first(); Applications initial = toApplications(first); when(requestHandler.getApplications(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, initial).type(MediaType.APPLICATION_JSON_TYPE).build() ); EurekaClient client = discoveryClientResource.getClient(); assertThat(client.getApplications().getAppsHashCode(), is(equalTo("UP_1_"))); // Delta with one add InstanceInfo second = new InstanceInfo.Builder(instanceGen.take(1)).setInstanceId("foo1").setStatus(InstanceStatus.DOWN).setDataCenterInfo(null).build(); InstanceInfo third = new InstanceInfo.Builder(instanceGen.take(1)).setInstanceId("foo2").setStatus(InstanceStatus.UP).setDataCenterInfo(new DataCenterInfo() { @Override public Name getName() { return null; } }).build(); Applications delta = toApplications(second, third); delta.setAppsHashCode("DOWN_1_UP_2_"); when(requestHandler.getDelta(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, delta).type(MediaType.APPLICATION_JSON_TYPE).build() ); assertThat(discoveryClientResource.awaitCacheUpdate(5, TimeUnit.SECONDS), is(true)); assertThat(client.getApplications().getAppsHashCode(), is(equalTo("DOWN_1_UP_2_"))); } /** * There is a bug, because of which remote registry data structures are not initialized during full registry fetch, only during delta. */ private void prepareRemoteRegionRegistry() throws Exception { Applications localApplications = InstanceInfoGenerator.newBuilder(4, "app1", "app2").build().toApplications(); Applications remoteApplications = InstanceInfoGenerator.newBuilder(4, "remote1", "remote2").withZone(TEST_REMOTE_ZONE).build().toApplications(); Applications allApplications = mergeApplications(localApplications, remoteApplications); // Load remote data in delta, to go around exiting bug in DiscoveryClient Applications delta = copyApplications(remoteApplications); delta.setAppsHashCode(allApplications.getAppsHashCode()); when(requestHandler.getApplications(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, localApplications).type(MediaType.APPLICATION_JSON_TYPE).build() ); when(requestHandler.getDelta(TEST_REMOTE_REGION)).thenReturn( anEurekaHttpResponse(200, delta).type(MediaType.APPLICATION_JSON_TYPE).build() ); assertThat(discoveryClientResource.awaitCacheUpdate(5, TimeUnit.SECONDS), is(true)); } }