package com.netflix.eureka.cluster; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.converters.EurekaJacksonCodec; import com.netflix.discovery.shared.transport.ClusterSampleData; import com.netflix.discovery.shared.transport.EurekaHttpResponse; import com.netflix.eureka.DefaultEurekaServerConfig; import com.netflix.eureka.EurekaServerConfig; import com.netflix.eureka.resources.ASGResource.ASGStatus; import com.netflix.eureka.resources.DefaultServerCodecs; import com.netflix.eureka.resources.ServerCodecs; import com.netflix.eureka.transport.JerseyReplicationClient; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockserver.client.server.MockServerClient; import org.mockserver.junit.MockServerRule; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockserver.model.Header.header; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; /** * Ideally we would test client/server REST layer together as an integration test, where server side has mocked * service layer. Right now server side REST has to much logic, so this test would be equal to testing everything. * Here we test only client side REST communication. * * @author Tomasz Bak */ public class JerseyReplicationClientTest { @Rule public MockServerRule serverMockRule = new MockServerRule(this); private MockServerClient serverMockClient; private JerseyReplicationClient replicationClient; private final EurekaServerConfig config = new DefaultEurekaServerConfig(); private final ServerCodecs serverCodecs = new DefaultServerCodecs(config); private final InstanceInfo instanceInfo = ClusterSampleData.newInstanceInfo(1); @Before public void setUp() throws Exception { replicationClient = JerseyReplicationClient.createReplicationClient( config, serverCodecs, "http://localhost:" + serverMockRule.getHttpPort() + "/eureka/v2" ); } @After public void tearDown() { if (serverMockClient != null) { serverMockClient.reset(); } } @Test public void testRegistrationReplication() throws Exception { serverMockClient.when( request() .withMethod("POST") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/apps/" + instanceInfo.getAppName()) ).respond( response().withStatusCode(200) ); EurekaHttpResponse<Void> response = replicationClient.register(instanceInfo); assertThat(response.getStatusCode(), is(equalTo(200))); } @Test public void testCancelReplication() throws Exception { serverMockClient.when( request() .withMethod("DELETE") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/apps/" + instanceInfo.getAppName() + '/' + instanceInfo.getId()) ).respond( response().withStatusCode(204) ); EurekaHttpResponse<Void> response = replicationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId()); assertThat(response.getStatusCode(), is(equalTo(204))); } @Test public void testHeartbeatReplicationWithNoResponseBody() throws Exception { serverMockClient.when( request() .withMethod("PUT") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/apps/" + instanceInfo.getAppName() + '/' + instanceInfo.getId()) ).respond( response().withStatusCode(200) ); EurekaHttpResponse<InstanceInfo> response = replicationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, InstanceStatus.DOWN); assertThat(response.getStatusCode(), is(equalTo(200))); assertThat(response.getEntity(), is(nullValue())); } @Test public void testHeartbeatReplicationWithResponseBody() throws Exception { InstanceInfo remoteInfo = new InstanceInfo(this.instanceInfo); remoteInfo.setStatus(InstanceStatus.DOWN); byte[] responseBody = toGzippedJson(remoteInfo); serverMockClient.when( request() .withMethod("PUT") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/apps/" + this.instanceInfo.getAppName() + '/' + this.instanceInfo.getId()) ).respond( response() .withStatusCode(Status.CONFLICT.getStatusCode()) .withHeader(header("Content-Type", MediaType.APPLICATION_JSON)) .withHeader(header("Content-Encoding", "gzip")) .withBody(responseBody) ); EurekaHttpResponse<InstanceInfo> response = replicationClient.sendHeartBeat(this.instanceInfo.getAppName(), this.instanceInfo.getId(), this.instanceInfo, null); assertThat(response.getStatusCode(), is(equalTo(Status.CONFLICT.getStatusCode()))); assertThat(response.getEntity(), is(notNullValue())); } @Test public void testAsgStatusUpdateReplication() throws Exception { serverMockClient.when( request() .withMethod("PUT") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/asg/" + instanceInfo.getASGName() + "/status") ).respond( response().withStatusCode(200) ); EurekaHttpResponse<Void> response = replicationClient.statusUpdate(instanceInfo.getASGName(), ASGStatus.ENABLED); assertThat(response.getStatusCode(), is(equalTo(200))); } @Test public void testStatusUpdateReplication() throws Exception { serverMockClient.when( request() .withMethod("PUT") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/apps/" + instanceInfo.getAppName() + '/' + instanceInfo.getId() + "/status") ).respond( response().withStatusCode(200) ); EurekaHttpResponse<Void> response = replicationClient.statusUpdate(instanceInfo.getAppName(), instanceInfo.getId(), InstanceStatus.DOWN, instanceInfo); assertThat(response.getStatusCode(), is(equalTo(200))); } @Test public void testDeleteStatusOverrideReplication() throws Exception { serverMockClient.when( request() .withMethod("DELETE") .withHeader(header(PeerEurekaNode.HEADER_REPLICATION, "true")) .withPath("/eureka/v2/apps/" + instanceInfo.getAppName() + '/' + instanceInfo.getId() + "/status") ).respond( response().withStatusCode(204) ); EurekaHttpResponse<Void> response = replicationClient.deleteStatusOverride(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo); assertThat(response.getStatusCode(), is(equalTo(204))); } private static byte[] toGzippedJson(InstanceInfo remoteInfo) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(bos); EurekaJacksonCodec.getInstance().writeTo(remoteInfo, gos); gos.flush(); return bos.toByteArray(); } }