package cf.spring; import cf.component.VarzProducer; import cf.nats.CfNats; import cf.nats.DefaultCfNats; import cf.nats.Publication; import cf.nats.PublicationHandler; import cf.nats.RequestResponseHandler; import cf.nats.message.ComponentAnnounce; import cf.nats.message.ComponentDiscover; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import nats.client.MockNats; import nats.client.Nats; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.testng.annotations.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.concurrent.TimeUnit; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; /** * @author Mike Heath */ public class CfComponentTest { @EnableAutoConfiguration @Configuration @CfComponent(type = "Test") static class TestConfiguration { final Queue<ComponentAnnounce> componentAnnouncements = new LinkedList<>(); @Bean Nats mockNats() { return new MockNats(); } @Bean CfNats nats() { final DefaultCfNats nats = new DefaultCfNats(mockNats()); nats.subscribe(ComponentAnnounce.class, new PublicationHandler<ComponentAnnounce, Void>() { @Override public void onMessage(Publication<ComponentAnnounce, Void> publication) { componentAnnouncements.add(publication.getMessageBody()); } }); return nats; } } @Test public void nats() { try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class)) { final ComponentAnnounce natsAnnouncement = getComponentAnnounce(context); assertNotNull(natsAnnouncement); assertEquals(natsAnnouncement.getType(), "Test"); context.getBean(CfNats.class).request(new ComponentDiscover(), 1, TimeUnit.SECONDS, new RequestResponseHandler<ComponentAnnounce>() { @Override public void onResponse(Publication<ComponentAnnounce, Void> response) { assertEquals(response.getMessageBody().getType(), "Test"); } }); } } @Test public void publishesHealthz() throws Exception { final SpringApplication application = new SpringApplication(TestConfiguration.class); try (ConfigurableApplicationContext context = application.run()) { final ComponentAnnounce natsAnnouncement = getComponentAnnounce(context); final URL url = new URL("http://" + natsAnnouncement.getHost() + "/healthz"); final InputStream in = (InputStream) url.getContent(); final String content = new BufferedReader(new InputStreamReader(in)).readLine(); assertEquals("ok", content); } } @Test(expectedExceptions = IOException.class) public void varzRequiresAuthentication() throws Exception { final SpringApplication application = new SpringApplication(TestConfiguration.class); try (ConfigurableApplicationContext context = application.run()) { final ComponentAnnounce natsAnnouncement = getComponentAnnounce(context); new URL("http://foo:bar@" + natsAnnouncement.getHost() + "/varz").getContent(); fail("Should have thrown authentication exception."); } } @Test public void varz() throws Exception { final SpringApplication application = new SpringApplication(TestConfiguration.class); try (ConfigurableApplicationContext context = application.run()) { final CfComponentConfiguration componentConfiguration = context.getBean(CfComponentConfiguration.class); final ComponentAnnounce natsAnnouncement = getComponentAnnounce(context); final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(componentConfiguration.getUsername(), componentConfiguration.getPassword())); HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build(); HttpGet get = new HttpGet("http://" + natsAnnouncement.getHost() + "/varz"); final HttpResponse response = client.execute(get); assertEquals(200, response.getStatusLine().getStatusCode()); final JsonNode node = new ObjectMapper().readTree(response.getEntity().getContent()); assertEquals(node.get("type").asText(), "Test"); assertEquals(node.get("index").asInt(), componentConfiguration.getIndex()); assertEquals(node.get("uuid").asText(), componentConfiguration.getUuid()); assertTrue(node.has("num_cores")); assertTrue(node.has("num_cores")); } } @Configuration static class ExtraVarzConfiguration { @Bean VarzProducer extraProducer() { return new VarzProducer() { @Override public Map<String, JsonNode> produceVarz() { final Map<String, JsonNode> varz = new HashMap<>(); varz.put("test", JsonNodeFactory.instance.textNode("value")); return varz; } }; } } @Test public void extraVarz() throws Exception { final SpringApplication application = new SpringApplication(TestConfiguration.class, ExtraVarzConfiguration.class); try (ConfigurableApplicationContext context = application.run()) { final CfComponentConfiguration componentConfiguration = context.getBean(CfComponentConfiguration.class); final ComponentAnnounce natsAnnouncement = getComponentAnnounce(context); final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(componentConfiguration.getUsername(), componentConfiguration.getPassword())); HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build(); HttpGet get = new HttpGet("http://" + natsAnnouncement.getHost() + "/varz"); final HttpResponse response = client.execute(get); assertEquals(200, response.getStatusLine().getStatusCode()); final JsonNode node = new ObjectMapper().readTree(response.getEntity().getContent()); assertEquals(node.get("type").asText(), "Test"); assertEquals(node.get("test").asText(), "value"); } } private ComponentAnnounce getComponentAnnounce(ConfigurableApplicationContext context) { final TestConfiguration configuration = context.getBean(TestConfiguration.class); return configuration.componentAnnouncements.poll(); } }