diff options
3 files changed, 439 insertions, 157 deletions
diff --git a/feature-healthcheck/pom.xml b/feature-healthcheck/pom.xml index 17a5053a..1fc853e8 100644 --- a/feature-healthcheck/pom.xml +++ b/feature-healthcheck/pom.xml @@ -18,147 +18,147 @@ ============LICENSE_END========================================================= --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - <modelVersion>4.0.0</modelVersion> - - <parent> - <groupId>org.onap.policy.drools-pdp</groupId> - <artifactId>drools-pdp</artifactId> - <version>1.3.0-SNAPSHOT</version> - </parent> - - <artifactId>feature-healthcheck</artifactId> - - <name>feature-healthcheck</name> - <description>Loadable module that performs remote system healthchecks</description> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>drools-pdp</artifactId> + <version>1.3.0-SNAPSHOT</version> + </parent> + + <artifactId>feature-healthcheck</artifactId> + + <name>feature-healthcheck</name> + <description>Loadable module that performs remote system healthchecks</description> - <properties> - <maven.compiler.source>1.8</maven.compiler.source> - <maven.compiler.target>1.8</maven.compiler.target> - </properties> + <properties> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> - <build> - <plugins> - <plugin> - <artifactId>maven-assembly-plugin</artifactId> - <executions> - <execution> - <id>zipfile</id> - <goals> - <goal>single</goal> - </goals> - <phase>package</phase> - <configuration> - <attach>true</attach> - <finalName>${project.artifactId}-${project.version}</finalName> - <descriptors> - <descriptor>src/assembly/assemble_zip.xml</descriptor> - </descriptors> - <appendAssemblyId>false</appendAssemblyId> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>copy-dependencies</id> - <goals> - <goal>copy-dependencies</goal> - </goals> - <phase>prepare-package</phase> - <configuration> - <transitive>false</transitive> - <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory> - <overWriteReleases>false</overWriteReleases> - <overWriteSnapshots>true</overWriteSnapshots> - <overWriteIfNewer>true</overWriteIfNewer> - <useRepositoryLayout>false</useRepositoryLayout> - <addParentPoms>false</addParentPoms> - <copyPom>false</copyPom> - <includeScope>runtime</includeScope> - <excludeTransitive>true</excludeTransitive> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <artifactId>maven-checkstyle-plugin</artifactId> - <executions> - <execution> - <id>onap-java-style</id> - <goals> - <goal>check</goal> - </goals> - <phase>process-sources</phase> - <configuration> - <!-- Use Google Java Style Guide: https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml - with minor changes --> - <configLocation>onap-checkstyle/onap-java-style.xml</configLocation> - <!-- <sourceDirectory> is needed so that checkstyle ignores the generated sources directory --> - <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory> - <includeResources>true</includeResources> - <includeTestSourceDirectory>true</includeTestSourceDirectory> - <includeTestResources>true</includeTestResources> - <excludes> - </excludes> - <suppressionsLocation>${project.baseUri}checkstyle-suppressions.xml</suppressionsLocation> - <consoleOutput>true</consoleOutput> - <failsOnViolation>true</failsOnViolation> - <violationSeverity>warning</violationSeverity> - </configuration> - </execution> - </executions> - <dependencies> - <dependency> - <groupId>org.onap.oparent</groupId> - <artifactId>checkstyle</artifactId> - <version>${oparent.version}</version> - <scope>compile</scope> - </dependency> - </dependencies> - </plugin> - </plugins> - </build> + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>zipfile</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${project.artifactId}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>false</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeScope>runtime</includeScope> + <excludeTransitive>true</excludeTransitive> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-checkstyle-plugin</artifactId> + <executions> + <execution> + <id>onap-java-style</id> + <goals> + <goal>check</goal> + </goals> + <phase>process-sources</phase> + <configuration> + <!-- Use Google Java Style Guide: https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml + with minor changes --> + <configLocation>onap-checkstyle/onap-java-style.xml</configLocation> + <!-- <sourceDirectory> is needed so that checkstyle ignores the generated sources directory --> + <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory> + <includeResources>true</includeResources> + <includeTestSourceDirectory>true</includeTestSourceDirectory> + <includeTestResources>true</includeTestResources> + <excludes> + </excludes> + <suppressionsLocation>${project.baseUri}checkstyle-suppressions.xml</suppressionsLocation> + <consoleOutput>true</consoleOutput> + <failsOnViolation>true</failsOnViolation> + <violationSeverity>warning</violationSeverity> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.onap.oparent</groupId> + <artifactId>checkstyle</artifactId> + <version>${oparent.version}</version> + <scope>compile</scope> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> - <dependencies> - <dependency> - <groupId>io.swagger</groupId> - <artifactId>swagger-jersey2-jaxrs</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.onap.policy.drools-pdp</groupId> - <artifactId>policy-core</artifactId> - <version>${project.version}</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.onap.policy.common</groupId> - <artifactId>policy-endpoints</artifactId> - <version>${project.version}</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.onap.policy.drools-pdp</groupId> - <artifactId>policy-management</artifactId> - <version>${project.version}</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-api-mockito</artifactId> - <scope>test</scope> - </dependency> - </dependencies> + <dependencies> + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-jersey2-jaxrs</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>policy-core</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.common</groupId> + <artifactId>policy-endpoints</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>policy-management</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <scope>test</scope> + </dependency> + </dependencies> </project> diff --git a/feature-healthcheck/src/main/java/org/onap/policy/drools/healthcheck/HealthCheck.java b/feature-healthcheck/src/main/java/org/onap/policy/drools/healthcheck/HealthCheck.java index 40e4f354..36444f8e 100644 --- a/feature-healthcheck/src/main/java/org/onap/policy/drools/healthcheck/HealthCheck.java +++ b/feature-healthcheck/src/main/java/org/onap/policy/drools/healthcheck/HealthCheck.java @@ -7,9 +7,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,7 +28,9 @@ import javax.ws.rs.core.Response; import org.onap.policy.common.capabilities.Startable; import org.onap.policy.common.endpoints.http.client.HttpClient; +import org.onap.policy.common.endpoints.http.client.HttpClientFactory; import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory; import org.onap.policy.drools.persistence.SystemPersistence; import org.onap.policy.drools.system.PolicyEngine; import org.slf4j.Logger; @@ -168,7 +170,7 @@ public interface HealthCheck extends Startable { /** * Perform a healthcheck. - * + * * @return a report */ public Reports healthCheck(); @@ -186,17 +188,17 @@ class HealthCheckMonitor implements HealthCheck { private static Logger logger = LoggerFactory.getLogger(HealthCheckMonitor.class); /** - * attached http servers. + * Attached http servers. */ protected volatile List<HttpServletServer> servers = new ArrayList<>(); /** - * attached http clients. + * Attached http clients. */ protected volatile List<HttpClient> clients = new ArrayList<>(); /** - * healthcheck configuration. + * Healthcheck configuration. */ protected volatile Properties healthCheckProperties = null; @@ -206,14 +208,15 @@ class HealthCheckMonitor implements HealthCheck { @Override public Reports healthCheck() { Reports reports = new Reports(); - reports.setHealthy(PolicyEngine.manager.isAlive()); + boolean thisEngineIsAlive = getEngineManager().isAlive(); + reports.setHealthy(thisEngineIsAlive); HealthCheck.Report engineReport = new Report(); - engineReport.setHealthy(PolicyEngine.manager.isAlive()); + engineReport.setHealthy(thisEngineIsAlive); engineReport.setName("PDP-D"); engineReport.setUrl("self"); - engineReport.setCode(PolicyEngine.manager.isAlive() ? 200 : 500); - engineReport.setMessage(PolicyEngine.manager.isAlive() ? "alive" : "not alive"); + engineReport.setCode(thisEngineIsAlive ? 200 : 500); + engineReport.setMessage(thisEngineIsAlive ? "alive" : "not alive"); reports.getDetails().add(engineReport); for (HttpClient client : clients) { @@ -248,10 +251,9 @@ class HealthCheckMonitor implements HealthCheck { public boolean start() { try { - this.healthCheckProperties = - SystemPersistence.manager.getProperties(HealthCheckFeature.CONFIGURATION_PROPERTIES_NAME); - this.servers = HttpServletServer.factory.build(healthCheckProperties); - this.clients = HttpClient.factory.build(healthCheckProperties); + this.healthCheckProperties = getPersistentProperties(HealthCheckFeature.CONFIGURATION_PROPERTIES_NAME); + this.servers = getServerFactory().build(healthCheckProperties); + this.clients = getClientFactory().build(healthCheckProperties); for (HttpServletServer server : servers) { startServer(server); @@ -307,7 +309,7 @@ class HealthCheckMonitor implements HealthCheck { /** * Get servers. - * + * * @return list of attached Http Servers */ public List<HttpServletServer> getServers() { @@ -316,7 +318,7 @@ class HealthCheckMonitor implements HealthCheck { /** * Get clients. - * + * * @return list of attached Http Clients */ public List<HttpClient> getClients() { @@ -354,4 +356,21 @@ class HealthCheckMonitor implements HealthCheck { return builder.toString(); } + // the following methods may be overridden by junit tests + + protected PolicyEngine getEngineManager() { + return PolicyEngine.manager; + } + + protected HttpServletServerFactory getServerFactory() { + return HttpServletServer.factory; + } + + protected HttpClientFactory getClientFactory() { + return HttpClient.factory; + } + + protected Properties getPersistentProperties(String propertyName) { + return SystemPersistence.manager.getProperties(propertyName); + } } diff --git a/feature-healthcheck/src/test/java/org/onap/policy/drools/healthcheck/HealthCheckTest.java b/feature-healthcheck/src/test/java/org/onap/policy/drools/healthcheck/HealthCheckTest.java index 850b1edf..2bf1cca5 100644 --- a/feature-healthcheck/src/test/java/org/onap/policy/drools/healthcheck/HealthCheckTest.java +++ b/feature-healthcheck/src/test/java/org/onap/policy/drools/healthcheck/HealthCheckTest.java @@ -21,14 +21,28 @@ package org.onap.policy.drools.healthcheck; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.net.HttpURLConnection; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Properties; +import javax.ws.rs.core.Response; +import org.junit.Before; import org.junit.Test; +import org.onap.policy.common.endpoints.http.client.HttpClient; +import org.onap.policy.common.endpoints.http.client.HttpClientFactory; +import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory; import org.onap.policy.drools.healthcheck.HealthCheck.Report; import org.onap.policy.drools.healthcheck.HealthCheck.Reports; +import org.onap.policy.drools.system.PolicyEngine; public class HealthCheckTest { @@ -36,9 +50,62 @@ public class HealthCheckTest { private static final String RPT_MSG = "report-message"; private static final String RPT_NAME = "report-name"; private static final String RPT_URL = "report-url"; + private static final String EXPECTED = "expected exception"; + + private static final String CLIENT_NAME1 = "name-a"; + private static final String CLIENT_URL1 = "url-a"; + private static final String CLIENT_NAME2 = "name-b"; + private static final String CLIENT_URL2 = "url-b"; + private static final String CLIENT_NAME3 = "name-c"; + private static final String CLIENT_URL3 = "url-c"; + + private Properties properties; + private HttpServletServerFactory servletFactory; + private HttpServletServer server1; + private HttpServletServer server2; + private HttpClientFactory clientFactory; + private HttpClient client1; + private HttpClient client2; + private HttpClient client3; + private List<HttpServletServer> servers; + private List<HttpClient> clients; + private PolicyEngine engineMgr; + private HealthCheckMonitor monitor; + + /** + * Initializes the object to be tested. + * + * @throws Exception if an error occurs + */ + @Before + public void setUp() throws Exception { + properties = new Properties(); + servletFactory = mock(HttpServletServerFactory.class); + server1 = mock(HttpServletServer.class); + server2 = mock(HttpServletServer.class); + clientFactory = mock(HttpClientFactory.class); + client1 = mock(HttpClient.class); + client2 = mock(HttpClient.class); + client3 = mock(HttpClient.class); + servers = Arrays.asList(server1, server2); + clients = Arrays.asList(client1, client2, client3); + engineMgr = mock(PolicyEngine.class); + + when(client1.getName()).thenReturn(CLIENT_NAME1); + when(client1.getBaseUrl()).thenReturn(CLIENT_URL1); + when(client2.getName()).thenReturn(CLIENT_NAME2); + when(client2.getBaseUrl()).thenReturn(CLIENT_URL2); + when(client3.getName()).thenReturn(CLIENT_NAME3); + when(client3.getBaseUrl()).thenReturn(CLIENT_URL3); + when(servletFactory.build(properties)).thenReturn(servers); + when(clientFactory.build(properties)).thenReturn(clients); + when(engineMgr.isAlive()).thenReturn(true); + + monitor = new HealthCheckMonitorImpl(); + } @Test - public void testHealthCheck_Report() { + public void testReport() { Report rpt = new Report(); // toString should work with un-populated data @@ -65,7 +132,7 @@ public class HealthCheckTest { } @Test - public void testHealthCheck_Reports() { + public void testReports() { Reports reports = new Reports(); // toString should work with un-populated data @@ -86,4 +153,200 @@ public class HealthCheckTest { assertNotNull(reports.toString()); } + @Test + public void testHealthCheckMonitor_HealthCheck() { + monitor.start(); + + // first client is healthy + Response resp = mock(Response.class); + when(resp.getStatus()).thenReturn(HttpURLConnection.HTTP_OK); + when(resp.readEntity(String.class)).thenReturn(RPT_MSG); + when(client1.get()).thenReturn(resp); + + // second client throws an exception + when(client2.get()).thenThrow(new RuntimeException(EXPECTED)); + + // third client is not healthy + resp = mock(Response.class); + when(resp.getStatus()).thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR); + when(resp.readEntity(String.class)).thenReturn(RPT_NAME); + when(client3.get()).thenReturn(resp); + + Reports reports = monitor.healthCheck(); + assertNotNull(reports); + assertEquals(4, reports.getDetails().size()); + assertFalse(reports.isHealthy()); + + int index = 0; + + Report report = reports.getDetails().get(index++); + assertEquals(true, report.isHealthy()); + assertEquals("PDP-D", report.getName()); + assertEquals("self", report.getUrl()); + assertEquals("alive", report.getMessage()); + assertEquals(HttpURLConnection.HTTP_OK, report.getCode()); + + report = reports.getDetails().get(index++); + assertEquals(true, report.isHealthy()); + assertEquals(client1.getName(), report.getName()); + assertEquals(client1.getBaseUrl(), report.getUrl()); + assertEquals(RPT_MSG, report.getMessage()); + assertEquals(HttpURLConnection.HTTP_OK, report.getCode()); + + report = reports.getDetails().get(index++); + assertEquals(false, report.isHealthy()); + assertEquals(client2.getName(), report.getName()); + assertEquals(client2.getBaseUrl(), report.getUrl()); + + report = reports.getDetails().get(index++); + assertEquals(false, report.isHealthy()); + assertEquals(client3.getName(), report.getName()); + assertEquals(client3.getBaseUrl(), report.getUrl()); + assertEquals(RPT_NAME, report.getMessage()); + assertEquals(HttpURLConnection.HTTP_INTERNAL_ERROR, report.getCode()); + + // indicate that engine is no longer healthy and re-run health check + when(engineMgr.isAlive()).thenReturn(false); + + reports = monitor.healthCheck(); + report = reports.getDetails().get(0); + + assertEquals(false, report.isHealthy()); + assertEquals("not alive", report.getMessage()); + assertEquals(500, report.getCode()); + } + + @Test + public void testHealthCheckMonitor_Start() { + // arrange for one server to throw an exception + when(server1.start()).thenThrow(new RuntimeException(EXPECTED)); + + assertTrue(monitor.start()); + + verify(server1).start(); + verify(server2).start(); + + /* + * Generate exception during building. + */ + + // new monitor + monitor = new HealthCheckMonitorImpl() { + @Override + protected HttpServletServerFactory getServerFactory() { + throw new RuntimeException(EXPECTED); + } + }; + + assertFalse(monitor.start()); + } + + @Test + public void testHealthCheckMonitor_Stop() { + monitor.start(); + + // arrange for one server and one client to throw an exception + when(server1.stop()).thenThrow(new RuntimeException(EXPECTED)); + when(client2.stop()).thenThrow(new RuntimeException(EXPECTED)); + + assertTrue(monitor.stop()); + + verify(server1).stop(); + verify(server2).stop(); + verify(client1).stop(); + verify(client2).stop(); + verify(client3).stop(); + } + + @Test + public void testHealthCheckMonitor_Shutdown() { + monitor.start(); + monitor.shutdown(); + + // at least one "stop" should have been called + verify(server1).stop(); + } + + @Test + public void testHealthCheckMonitor_IsAlive() { + assertFalse(monitor.isAlive()); + + monitor.start(); + assertTrue(monitor.isAlive()); + } + + @Test + public void testHealthCheckMonitor_GetServers_GetClients() { + monitor.start(); + assertEquals(servers, monitor.getServers()); + assertEquals(clients, monitor.getClients()); + } + + @Test + public void testHealthCheckMonitor_GetHttpBody() { + Response response = mock(Response.class); + when(response.readEntity(String.class)).thenReturn(RPT_MSG); + assertEquals(RPT_MSG, monitor.getHttpBody(response, client1)); + + // readEntity() throws an exception + when(response.readEntity(String.class)).thenThrow(new RuntimeException(EXPECTED)); + assertEquals(null, monitor.getHttpBody(response, client1)); + } + + @Test + public void testHealthCheckMonitor_StartServer() { + monitor.startServer(server1); + verify(server1).start(); + + // force start() to throw an exception - monitor should still work + when(server1.start()).thenThrow(new RuntimeException(EXPECTED)); + monitor.startServer(server1); + } + + @Test + public void testHealthCheckMonitor_ToString() { + assertTrue(monitor.toString().startsWith("HealthCheckMonitor [")); + } + + @Test + public void testHealthCheckMonitor_GetEngineManager() { + assertNotNull(new HealthCheckMonitor().getEngineManager()); + } + + @Test + public void testHealthCheckMonitor_GetServerFactory() { + assertNotNull(new HealthCheckMonitor().getServerFactory()); + } + + @Test + public void testHealthCheckMonitor_GetClientFactory() { + assertNotNull(new HealthCheckMonitor().getClientFactory()); + } + + /** + * Monitor with overrides. + */ + private class HealthCheckMonitorImpl extends HealthCheckMonitor { + + @Override + protected PolicyEngine getEngineManager() { + return engineMgr; + } + + @Override + protected HttpServletServerFactory getServerFactory() { + return servletFactory; + } + + @Override + protected HttpClientFactory getClientFactory() { + return clientFactory; + } + + @Override + protected Properties getPersistentProperties(String propertyName) { + return properties; + } + + } } |